Class: DataShift::Binder

Inherits:
Object
  • Object
show all
Extended by:
Delimiters
Includes:
Delimiters, Logging
Defined in:
lib/datashift/binder.rb

Instance Attribute Summary collapse

Attributes included from Delimiters

#attribute_list_end, #attribute_list_start, #csv_delimiter, #key_value_sep, #text_delim

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Delimiters

column_delim, column_delim=, eol, multi_assoc_delim, multi_assoc_delim=, multi_facet_delim, multi_value_delim, multi_value_delim=, name_value_delim, name_value_delim=, setmulti_facet_delim

Methods included from Logging

#logdir, #logdir=, #logger, #verbose

Constructor Details

#initializeBinder

Returns a new instance of Binder.



39
40
41
# File 'lib/datashift/binder.rb', line 39

def initialize
  reset
end

Instance Attribute Details

#bindingsObject

Returns the value of attribute bindings.



37
38
39
# File 'lib/datashift/binder.rb', line 37

def bindings
  @bindings
end

#missing_bindingsObject

Returns the value of attribute missing_bindings.



37
38
39
# File 'lib/datashift/binder.rb', line 37

def missing_bindings
  @missing_bindings
end

Class Method Details

.substitutions(external_name) ⇒ Object

TODO: - check out regexp to do this work better plus Inflections ?? Want to be able to handle any of [“Count On hand”, ‘count_on_hand’, “Count OnHand”, “COUNT ONHand” etc]



188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/datashift/binder.rb', line 188

def self.substitutions(external_name)
  name = external_name.to_s

  [
    name.downcase,
    name.tableize,
    name.tr(' ', '_'),
    name.tr(' ', '_').downcase,
    name.gsub(/(\s+)/, '_').downcase,
    name.delete(' '),
    name.delete(' ').downcase,
    name.tr(' ', '_').underscore
  ]
end

Instance Method Details

#add_bindings_from_nodes(nodes) ⇒ Object



169
170
171
172
# File 'lib/datashift/binder.rb', line 169

def add_bindings_from_nodes( nodes )
  logger.debug("Adding  [#{nodes.size}] custom bindings")
  nodes.each { |n| bindings << n.method_binding }
end

#add_missing(col_data, col_index, reason) ⇒ Object



177
178
179
180
181
182
183
184
# File 'lib/datashift/binder.rb', line 177

def add_missing(col_data, col_index, reason)
  logger.warn(reason)

  missing = NoMethodBinding.new(col_data, col_index, reason: reason)

  missing_bindings << missing
  bindings << missing
end

#forcedObject



64
65
66
# File 'lib/datashift/binder.rb', line 64

def forced
  forced_inclusion_columns.compact.collect { |f| f.to_s.downcase }
end

#forced?(column_name) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/datashift/binder.rb', line 68

def forced?(column_name)
  forced.include?(column_name.to_s.downcase)
end

#forced_inclusion_columnsObject



60
61
62
# File 'lib/datashift/binder.rb', line 60

def forced_inclusion_columns
  [*DataShift::Configuration.call.force_inclusion_of_columns]
end

#headers_missing_bindingsObject



52
53
54
# File 'lib/datashift/binder.rb', line 52

def headers_missing_bindings
  missing_bindings.collect(&:source)
end

#include_all?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/datashift/binder.rb', line 72

def include_all?
  DataShift::Configuration.call.include_all_columns == true
end

#indexes_missing_bindingsObject



56
57
58
# File 'lib/datashift/binder.rb', line 56

def indexes_missing_bindings
  missing_bindings.collect(&:index)
end

#map_inbound_headers(klass, columns) ⇒ Object Also known as: map_inbound_fields

Build complete picture of the methods whose names listed in columns Handles method names as defined by a user, from spreadsheets or file headers where the names specified may not be exactly as required e.g handles capitalisation, white space, _ etc

The header can also contain the fields to use in lookups, separated with Delimiters ::column_delim For example specify that lookups on has_one association called ‘product’, be performed using name’

product:name

The header can also contain a default value for the lookup field, again separated with Delimiters ::column_delim

For example specify lookups on assoc called ‘user’, be performed using ‘email’ == ‘[email protected]

user:email:test@blah.com

Returns: Array of matching method_details, including nils for non matched items

N.B Columns that could not be mapped are left in the array as NIL

This is to support clients that need to map via the index on @method_details

Other callers can simply call compact on the results if the index not important.

The MethodDetails instance will contain a pointer to the column index from which it was mapped.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/datashift/binder.rb', line 100

def map_inbound_headers(klass, columns)

  # If klass not in Dictionary yet, add to dictionary all possible operators on klass
  # which can be used to map headers and populate an object of type klass
  model_method_mgr = ModelMethods::Manager.catalog_class(klass)

  [*columns].each_with_index do |col_data, col_index|
    raw_col_data = col_data.to_s.strip

    if raw_col_data.nil? || raw_col_data.empty?
      logger.warn("Column list contains empty or null header at index #{col_index}")
      bindings << NoMethodBinding.new(raw_col_data, col_index)
      next
    end

    # Header DSL Name::Where::Value:Misc
    # For example :
    #     product_properties:name:test_pp_003
    #       => ProductProperty.where()name: "test_pp_003")
    #
    raw_col_name, where_field, where_value, *data = raw_col_data.split(column_delim).map(&:strip)

    # Find the domain model method details
    model_method = model_method_mgr.search(raw_col_name)

    # No such column, but if config set to include it, for example for delegated methods, add as op type :assignment
    if( model_method.nil? && (include_all? || forced?(raw_col_name)) )
      logger.debug("Operator #{raw_col_name} not found but forced inclusion set - adding as :assignment")
      model_method = model_method_mgr.insert(raw_col_name, :assignment)
    end

    unless model_method
      Binder.substitutions(raw_col_name).each do |n|
        model_method = model_method_mgr.search(n)
        break if model_method
      end
    end

    if(model_method)

      binding = MethodBinding.new(raw_col_name, col_index, model_method)

      # we slurped up all possible data in split, turn it back into original string
      binding.add_column_data(data.join(column_delim))

      if where_field
        logger.info("Lookup query field [#{where_field}] - specified for association #{model_method.operator}")

        begin
          binding.add_lookup(model_method, where_field, where_value)
        rescue => e
          logger.error(e.message)
          add_missing(raw_col_data, col_index, "Field [#{where_field}] Not Found for [#{raw_col_name}] (#{model_method.operator})")
          next
        end

      end

      logger.debug("Column [#{raw_col_data}] (#{col_index}) - mapped to :\n#{model_method.pp}")

      bindings << binding
    else
      add_missing(raw_col_data, col_index, "No operator or association found for Header [#{raw_col_name}]")
    end
  end

  bindings
end

#method_namesObject

The raw client supplied names



204
205
206
# File 'lib/datashift/binder.rb', line 204

def method_names
  bindings.collect( &:source )
end

#missing_bindings?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/datashift/binder.rb', line 48

def missing_bindings?
  !missing_bindings.empty?
end

#operator_namesObject

The true operator names discovered from model



209
210
211
# File 'lib/datashift/binder.rb', line 209

def operator_names
  bindings.collect( &:operator )
end

#resetObject



43
44
45
46
# File 'lib/datashift/binder.rb', line 43

def reset
  @bindings = []
  @missing_bindings = []
end