Class: ARLoader::LoaderBase

Inherits:
Object show all
Defined in:
lib/loaders/loader_base.rb

Direct Known Subclasses

CsvLoader, ExcelLoader, ImageLoader, ProductLoader

Constant Summary collapse

@@name_value_delim =

Enable an entry representing an association to contain multiple lookup name/value sets in default form : Name1:value1, value2|Name2:value1, value2, value3|Name3:value1, value2

E.G. Row for association could have a columns called Size and once called Colour, and this combination could be used to lookup multiple associations to add to the main model

  Size:small            # => generates find_by_size( 'small' )
  Size:large|           # => generates find_by_size( 'large' )
  Colour:red,green,blue # => generates find_all_by_colour( ['red','green','blue'] )

TODO - support embedded object creation/update via hash (which hopefully we should be able to just forward to AR)

 |Category|
 name:new{ :date => '20110102', :owner = > 'blah'}
':'
@@multi_value_delim =
','
@@multi_assoc_delim =

TODO - support multi embedded object creation/update via hash (which hopefully we should be able to just forward to AR)

 |Category|
 name:new{ :a => 1, :b => 2}|name:medium{ :a => 6, :b => 34}|name:old{ :a => 12, :b => 67}
'|'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(object_class, object = nil, options = {}) ⇒ LoaderBase

Options



57
58
59
60
61
62
63
64
65
66
# File 'lib/loaders/loader_base.rb', line 57

def initialize(object_class, object = nil, options = {})
  @load_object_class = object_class

  # Gather list of all possible 'setter' methods on AR class (instance variables and associations)
  ARLoader::MethodMapper.find_operators( @load_object_class )

  @options = options.clone
  @headers = []
  reset(object)
end

Instance Attribute Details

#current_method_detailObject

Returns the value of attribute current_method_detail.



20
21
22
# File 'lib/loaders/loader_base.rb', line 20

def current_method_detail
  @current_method_detail
end

#current_valueObject

Returns the value of attribute current_value.



20
21
22
# File 'lib/loaders/loader_base.rb', line 20

def current_value
  @current_value
end

#failed_objectsObject

Returns the value of attribute failed_objects.



22
23
24
# File 'lib/loaders/loader_base.rb', line 22

def failed_objects
  @failed_objects
end

#headersObject (readonly)

Returns the value of attribute headers.



17
18
19
# File 'lib/loaders/loader_base.rb', line 17

def headers
  @headers
end

#load_objectObject

Returns the value of attribute load_object.



19
20
21
# File 'lib/loaders/loader_base.rb', line 19

def load_object
  @load_object
end

#load_object_classObject

Returns the value of attribute load_object_class.



19
20
21
# File 'lib/loaders/loader_base.rb', line 19

def load_object_class
  @load_object_class
end

#loaded_objectsObject

Returns the value of attribute loaded_objects.



22
23
24
# File 'lib/loaders/loader_base.rb', line 22

def loaded_objects
  @loaded_objects
end

#optionsObject

Returns the value of attribute options.



24
25
26
# File 'lib/loaders/loader_base.rb', line 24

def options
  @options
end

Class Method Details

.set_multi_assoc_delim(x) ⇒ Object



54
# File 'lib/loaders/loader_base.rb', line 54

def self.set_multi_assoc_delim(x) @@multi_assoc_delim = x; end

.set_multi_value_delim(x) ⇒ Object



53
# File 'lib/loaders/loader_base.rb', line 53

def self.set_multi_value_delim(x) @@multi_value_delim = x; end

.set_name_value_delim(x) ⇒ Object

Used to delimit multiple entries in a single column



52
# File 'lib/loaders/loader_base.rb', line 52

def self.set_name_value_delim(x)  @@name_value_delim = x; end

Instance Method Details

#abort_on_failure?Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/loaders/loader_base.rb', line 88

def abort_on_failure?
  @options[:abort_on_failure] == 'true'
end

#contains_mandatory?(mandatory_list) ⇒ Boolean

Returns:

  • (Boolean)


74
75
76
77
# File 'lib/loaders/loader_base.rb', line 74

def contains_mandatory?( mandatory_list )
  puts "HEADERS", @headers
  [mandatory_list - @headers].flatten.empty?
end

#failed_countObject



96
97
98
# File 'lib/loaders/loader_base.rb', line 96

def failed_count
  @failed_objects.size
end

#find_and_process(klass, column_name, row_data) ⇒ Object

Search method mapper for supplied klass and column, and if suitable association found, process row data into current load_object



102
103
104
105
106
107
108
109
110
111
# File 'lib/loaders/loader_base.rb', line 102

def find_and_process(klass, column_name, row_data)
  method_detail = MethodMapper.find_method_detail( klass, column_name )

   
  if(method_detail)
    process(method_detail, row_data)
  else
    @load_object.errors.add_base( "No matching method found for column #{column_name}")
  end
end

#find_or_new(klass, condition_hash = {}) ⇒ Object



195
196
197
198
199
200
201
202
# File 'lib/loaders/loader_base.rb', line 195

def find_or_new( klass, condition_hash = {} )
  @records[klass] = klass.find(:all, :conditions => condition_hash)
  if @records[klass].any?
    return @records[klass].first
  else
    return klass.new
  end
end

#load(input, options = {}) ⇒ Object

kinda the derived classes interface - best way in Ruby ?



115
116
117
# File 'lib/loaders/loader_base.rb', line 115

def load( input, options = {} )
  raise "WARNING- ABSTRACT METHOD CALLED - Please implement load()"
end

#loaded_countObject



92
93
94
# File 'lib/loaders/loader_base.rb', line 92

def loaded_count
  @loaded_objects.size
end

#missing_mandatory(mandatory_list) ⇒ Object



79
80
81
# File 'lib/loaders/loader_base.rb', line 79

def missing_mandatory( mandatory_list )
  [mandatory_list - @headers].flatten
end

#new_load_objectObject



83
84
85
86
# File 'lib/loaders/loader_base.rb', line 83

def new_load_object
  @load_object = @load_object_class.new
  @load_object
end

#process(method_detail, value) ⇒ Object

What process a value string from a column. Assigning value(s) to correct association on @load_object. Method detail represents a column from a file and it's correlated AR associations. Value string which may contain multiple values for a collection association.



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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/loaders/loader_base.rb', line 125

def process(method_detail, value)
  #puts "INFO: LOADER BASE processing #{@load_object}"
  @current_value = value
  @current_method_detail = method_detail

  if(method_detail.operator_for(:has_many))

    if(method_detail.operator_class && @current_value)

      # there are times when we need to save early, for example before assigning to
      # has_and_belongs_to associations which require the load_object has an id for the join table
    
      save if( load_object.valid? && load_object.new_record? )

      # A single column can contain multiple associations delimited by special char
      columns = @current_value.split(@@multi_assoc_delim)

      # Size:large|Colour:red,green,blue   => generates find_by_size( 'large' ) and find_all_by_colour( ['red','green','blue'] )

      columns.each do |assoc|
        operator, values = assoc.split(@@name_value_delim)

        lookups = values.split(@@multi_value_delim)

        if(lookups.size > 1)

          @current_value = method_detail.operator_class.send("find_all_by_#{operator}", lookups )

          unless(lookups.size == @current_value.size)
            found = @current_value.collect {|f| f.send(operator) }
            @load_object.errors.add( method_detail.operator, "Association with key(s) #{(lookups - found).inspect} NOT found")
            puts "WARNING: Association with key(s) #{(lookups - found).inspect} NOT found - Not added."
            next if(@current_value.empty?)
          end

        else

          @current_value = method_detail.operator_class.send("find_by_#{operator}", lookups )

          unless(@current_value)
            @load_object.errors.add( method_detail.operator, "Association with key #{lookups} NOT found")
            puts "WARNING: Association with key #{lookups} NOT found - Not added."
            next
          end

        end

        # Lookup Assoc's Model done, now add the found value(s) to load model's collection
        method_detail.assign(@load_object, @current_value)
      end
    end
    # END HAS_MANY
  else
    # Nice n simple straight assignment to a column variable
    method_detail.assign(@load_object, @current_value)
  end
end

#reset(object = nil) ⇒ Object



68
69
70
71
72
# File 'lib/loaders/loader_base.rb', line 68

def reset(object = nil)
  @load_object = object || new_load_object
  @loaded_objects, @failed_objects = [],[]
  @current_value = nil
end

#saveObject



183
184
185
186
187
188
189
190
191
192
193
# File 'lib/loaders/loader_base.rb', line 183

def save
  puts "SAVING #{load_object.class} : #{load_object.inspect}" #if(options[:verbose])
  begin
    @load_object.save
    @loaded_objects << load_object unless(@loaded_objects.include?(load_object))
  rescue => e
    @failed_objects << load_object unless(@failed_objects.include?(load_object))
    puts "Error saving #{load_object.class} : #{e.inspect}"
    raise "Error in save whilst processing column #{@current_method_detail.name}" if(@options[:strict])
  end
end