datashift 0.10.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/datashift.gemspec +2 -2
- data/lib/datashift/method_dictionary.rb +4 -6
- data/lib/datashift/method_mapper.rb +7 -6
- data/lib/datashift/populator.rb +1 -1
- data/lib/loaders/csv_loader.rb +2 -2
- data/lib/loaders/excel_loader.rb +4 -9
- data/lib/loaders/loader_base.rb +13 -12
- data/lib/loaders/paperclip/attachment_loader.rb +30 -21
- data/lib/loaders/paperclip/datashift_paperclip.rb +5 -3
- data/lib/loaders/paperclip/image_loading.rb +10 -11
- data/lib/thor/import.thor +2 -2
- data/lib/thor/paperclip.thor +28 -9
- data/spec/excel_loader_spec.rb +5 -3
- data/spec/fixtures/db/datashift_test_models_db.sqlite +0 -0
- data/spec/method_dictionary_spec.rb +48 -5
- data/spec/paperclip_loader_spec.rb +2 -2
- metadata +41 -46
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.11.0
|
data/datashift.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "datashift"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.11.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Thomas Statter"]
|
12
|
-
s.date = "2012-10-
|
12
|
+
s.date = "2012-10-23"
|
13
13
|
s.description = "Comprehensive tools to import/export between Excel/CSV and ActiveRecord databases, Rails apps, and any Ruby project."
|
14
14
|
s.email = "rubygems@autotelik.co.uk"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -12,13 +12,11 @@ module DataShift
|
|
12
12
|
class MethodDictionary
|
13
13
|
|
14
14
|
include DataShift::Logging
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
# Has the dictionary been populated for klass
|
15
|
+
|
16
|
+
# Return true if dictionary has been populated for klass
|
20
17
|
def self.for?(klass)
|
21
|
-
|
18
|
+
any = has_many[klass] || belongs_to[klass] || has_one[klass] || assignments[klass]
|
19
|
+
return any != nil
|
22
20
|
end
|
23
21
|
|
24
22
|
# Create simple picture of all the operator names for assignment available on an AR model,
|
@@ -84,8 +84,7 @@ module DataShift
|
|
84
84
|
DataShift::MethodDictionary.build_method_details(klass)
|
85
85
|
end
|
86
86
|
|
87
|
-
forced = [*options[:force_inclusion]].compact
|
88
|
-
forced.collect! { |f| f.downcase }
|
87
|
+
forced = [*options[:force_inclusion]].compact.collect { |f| f.to_s.downcase }
|
89
88
|
|
90
89
|
@method_details, @missing_methods = [], []
|
91
90
|
|
@@ -94,7 +93,7 @@ module DataShift
|
|
94
93
|
raw_col_data = col_data.to_s
|
95
94
|
|
96
95
|
if(raw_col_data.nil? or raw_col_data.empty?)
|
97
|
-
logger.warn("Column list contains empty or null
|
96
|
+
logger.warn("Column list contains empty or null column at index #{col_index}")
|
98
97
|
@method_details << nil
|
99
98
|
next
|
100
99
|
end
|
@@ -103,10 +102,10 @@ module DataShift
|
|
103
102
|
|
104
103
|
md = MethodDictionary::find_method_detail( klass, raw_col_name )
|
105
104
|
|
106
|
-
# TODO be nice if we could
|
105
|
+
# TODO be nice if we could check that the assoc on klass responds to the specified
|
107
106
|
# lookup key now (nice n early)
|
108
107
|
# active_record_helper = "find_by_#{lookup}"
|
109
|
-
if(md.nil? && options[:include_all] || forced.include?(raw_col_name.downcase))
|
108
|
+
if(md.nil? && (options[:include_all] || forced.include?(raw_col_name.downcase)) )
|
110
109
|
md = MethodDictionary::add(klass, raw_col_name)
|
111
110
|
end
|
112
111
|
|
@@ -149,7 +148,9 @@ module DataShift
|
|
149
148
|
|
150
149
|
# Returns true if discovered methods contain every operator in mandatory_list
|
151
150
|
def contains_mandatory?( mandatory_list )
|
152
|
-
|
151
|
+
a = [*mandatory_list].collect { |f| f.downcase }
|
152
|
+
b = operator_names.collect { |f| f.downcase }
|
153
|
+
(a - b).empty?
|
153
154
|
end
|
154
155
|
|
155
156
|
def missing_mandatory( mandatory_list )
|
data/lib/datashift/populator.rb
CHANGED
@@ -31,7 +31,7 @@ module DataShift
|
|
31
31
|
record.send(op, value.send( f) )
|
32
32
|
break
|
33
33
|
rescue => e
|
34
|
-
|
34
|
+
puts "DEBUG: insistent_assignment: #{e.inspect}"
|
35
35
|
if f == Populator::insistent_method_list.last
|
36
36
|
puts "I'm sorry I have failed to assign [#{value}] to #{operator}"
|
37
37
|
raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
|
data/lib/loaders/csv_loader.rb
CHANGED
@@ -86,8 +86,8 @@ module DataShift
|
|
86
86
|
|
87
87
|
include DataShift::CsvLoading
|
88
88
|
|
89
|
-
def initialize(klass, object = nil, options = {})
|
90
|
-
super( klass, object, options )
|
89
|
+
def initialize(klass, find_operators = true, object = nil, options = {})
|
90
|
+
super( klass, find_operators, object, options )
|
91
91
|
raise "Cannot load - failed to create a #{klass}" unless @load_object
|
92
92
|
end
|
93
93
|
|
data/lib/loaders/excel_loader.rb
CHANGED
@@ -44,7 +44,6 @@ module DataShift
|
|
44
44
|
puts "\n\n\nLoading from Excel file: #{file_name}"
|
45
45
|
|
46
46
|
sheet_number = options[:sheet_number] || 0
|
47
|
-
|
48
47
|
|
49
48
|
@sheet = @excel.worksheet( sheet_number )
|
50
49
|
|
@@ -66,14 +65,11 @@ module DataShift
|
|
66
65
|
end
|
67
66
|
|
68
67
|
raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" if(@headers.empty?)
|
69
|
-
|
70
68
|
|
71
69
|
# Create a method_mapper which maps list of headers into suitable calls on the Active Record class
|
72
70
|
# For example if model has an attribute 'price' will map columns called Price, price, PRICE etc to this attribute
|
73
71
|
populate_method_mapper_from_headers( @headers, options )
|
74
72
|
|
75
|
-
@method_mapper
|
76
|
-
|
77
73
|
logger.info "Excel Loader processing #{@sheet.num_rows} rows"
|
78
74
|
|
79
75
|
loaded_objects.clear
|
@@ -105,10 +101,9 @@ module DataShift
|
|
105
101
|
|
106
102
|
# Iterate over method_details, working on data out of associated Excel column
|
107
103
|
@method_mapper.method_details.each do |method_detail|
|
108
|
-
|
109
|
-
|
104
|
+
|
110
105
|
next unless method_detail # TODO populate unmapped with a real MethodDetail that is 'null' and create is_nil
|
111
|
-
|
106
|
+
|
112
107
|
value = row[method_detail.column_index]
|
113
108
|
|
114
109
|
contains_data = true unless(value.nil? || value.to_s.empty?)
|
@@ -155,8 +150,8 @@ module DataShift
|
|
155
150
|
|
156
151
|
include ExcelLoading
|
157
152
|
|
158
|
-
def initialize(klass, object = nil, options = {})
|
159
|
-
super( klass, object, options )
|
153
|
+
def initialize(klass, find_operators = true, object = nil, options = {})
|
154
|
+
super( klass, find_operators, object, options )
|
160
155
|
raise "Cannot load - failed to create a #{klass}" unless @load_object
|
161
156
|
end
|
162
157
|
|
data/lib/loaders/loader_base.rb
CHANGED
@@ -39,25 +39,25 @@ module DataShift
|
|
39
39
|
#
|
40
40
|
# Options to drive building the method dictionary for a class, enabling headers to be mapped to operators on that class.
|
41
41
|
#
|
42
|
+
# find_operators [default = true] : Populate method dictionary with operators and method details
|
43
|
+
#
|
42
44
|
# Options
|
43
|
-
# :load [default = true] : Load the method dictionary for object_class
|
44
45
|
#
|
45
46
|
# :reload : Force load of the method dictionary for object_class even if already loaded
|
46
|
-
# :instance_methods : Include setter
|
47
|
-
|
48
|
-
|
49
|
-
def initialize(object_class, object = nil, options = {})
|
47
|
+
# :instance_methods : Include setter/delegate style instance methods for assignment, as well as AR columns
|
48
|
+
#
|
49
|
+
def initialize(object_class, find_operators = true, object = nil, options = {})
|
50
50
|
@load_object_class = object_class
|
51
|
-
|
51
|
+
|
52
52
|
# Gather names of all possible 'setter' methods on AR class (instance variables and associations)
|
53
|
-
|
54
|
-
|
53
|
+
if((find_operators && !MethodDictionary::for?(object_class)) || options[:reload])
|
54
|
+
puts "Building Method Dictionary for class #{object_class}"
|
55
55
|
DataShift::MethodDictionary.find_operators( @load_object_class, :reload => options[:reload], :instance_methods => options[:instance_methods] )
|
56
56
|
|
57
57
|
# Create dictionary of data on all possible 'setter' methods which can be used to
|
58
58
|
# populate or integrate an object of type @load_object_class
|
59
59
|
DataShift::MethodDictionary.build_method_details(@load_object_class)
|
60
|
-
end
|
60
|
+
end
|
61
61
|
|
62
62
|
@method_mapper = DataShift::MethodMapper.new
|
63
63
|
@config = options.dup # clone can cause issues like 'can't modify frozen hash'
|
@@ -152,10 +152,10 @@ module DataShift
|
|
152
152
|
raise MappingDefinitionError, "Missing mappings for columns : #{@method_mapper.missing_methods.join(",")}" if(strict)
|
153
153
|
end
|
154
154
|
|
155
|
-
unless(@method_mapper.contains_mandatory?(mandatory) )
|
156
|
-
@method_mapper.missing_mandatory(mandatory).each { |
|
155
|
+
unless(mandatory.empty? || @method_mapper.contains_mandatory?(mandatory) )
|
156
|
+
@method_mapper.missing_mandatory(mandatory).each { |er| puts "ERROR: Mandatory column missing - expected column '#{er}'" }
|
157
157
|
raise MissingMandatoryError, "Mandatory columns missing - please fix and retry."
|
158
|
-
end
|
158
|
+
end
|
159
159
|
|
160
160
|
@method_mapper
|
161
161
|
end
|
@@ -184,6 +184,7 @@ module DataShift
|
|
184
184
|
prepare_data(method_detail, data)
|
185
185
|
process()
|
186
186
|
else
|
187
|
+
puts "No matching method found for column #{column_name}"
|
187
188
|
@load_object.errors.add(:base, "No matching method found for column #{column_name}")
|
188
189
|
end
|
189
190
|
end
|
@@ -20,20 +20,16 @@ module DataShift
|
|
20
20
|
|
21
21
|
attr_writer :attach_to_field
|
22
22
|
attr_reader :attachment_path, :loading_files_cache
|
23
|
+
|
23
24
|
|
24
|
-
|
25
|
-
#
|
26
|
-
# return that otherwise return the raw format of :attach_to_find_by_field
|
27
|
-
|
28
|
-
def attach_to_field
|
29
|
-
@attach_to_method_detail || @attach_to_field
|
30
|
-
end
|
31
|
-
|
25
|
+
# Constructor
|
26
|
+
#
|
32
27
|
# Options
|
33
|
-
|
28
|
+
#
|
34
29
|
# => :attach_to_klass
|
35
|
-
# A class that has a relationship with
|
30
|
+
# A class that has a relationship with the attachment (has_many, has_one or belongs_to etc)
|
36
31
|
# The instance of :attach_to_klass can be searched for and the new attachment assigned.
|
32
|
+
#
|
37
33
|
# Examples
|
38
34
|
# Owner has_many pdfs and mp3 files as Digitals .... :attach_to_klass = Owner
|
39
35
|
# User has a single image used as an avatar ... :attach_to_klass = User
|
@@ -41,6 +37,7 @@ module DataShift
|
|
41
37
|
# => :attach_to_find_by_field
|
42
38
|
# For the :attach_to_klass, this is the field used to search for the parent
|
43
39
|
# object to assign the new attachment to.
|
40
|
+
#
|
44
41
|
# Examples
|
45
42
|
# Owner has a unique 'name' field ... :attach_to_find_by_field = :name
|
46
43
|
# User has a unique 'login' field ... :attach_to_klass = :login
|
@@ -48,27 +45,31 @@ module DataShift
|
|
48
45
|
# => :attach_to_field
|
49
46
|
# Attribute/association to assign attachment to on :attach_to_klass.
|
50
47
|
# Examples
|
48
|
+
#
|
51
49
|
# :attach_to_field => digitals : Owner.digitals = attachment
|
52
50
|
# :attach_to_field => avatar : User.avatar = attachment
|
53
51
|
#
|
54
52
|
#
|
55
|
-
def initialize(attachment_klazz, attachment = nil, options = {})
|
53
|
+
def initialize(attachment_klazz, find_operators = true, attachment = nil, options = {})
|
56
54
|
|
57
55
|
init_from_options( options )
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
super( attachment_klazz, attachment, opts )
|
56
|
+
|
57
|
+
super( attachment_klazz, find_operators, attachment, options.dup )
|
62
58
|
|
63
59
|
puts "Attachment Class is #{load_object_class}" if(@verbose)
|
64
60
|
|
65
61
|
raise "Failed to create Attachment for loading" unless @load_object
|
66
62
|
end
|
63
|
+
|
67
64
|
|
65
|
+
# Options
|
66
|
+
# :reload
|
67
|
+
# :attach_to_klass, :attach_to_field, :attach_to_find_by_field
|
68
|
+
#
|
68
69
|
def init_from_options( options )
|
69
|
-
|
70
|
-
@attach_to_klass = options[:attach_to_klass] || @attach_to_klass || nil
|
71
70
|
|
71
|
+
@attach_to_klass = options[:attach_to_klass] || @attach_to_klass || nil
|
72
|
+
|
72
73
|
unless(@attach_to_klass.nil? || (MethodDictionary::for?(@attach_to_klass) && options[:reload] == false))
|
73
74
|
#puts "Building Method Dictionary for class #{object_class}"
|
74
75
|
DataShift::MethodDictionary.find_operators( @attach_to_klass, :reload => options[:reload], :instance_methods => true )
|
@@ -86,6 +87,15 @@ module DataShift
|
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
90
|
+
|
91
|
+
# If we have instantiated a method detail based on the attach to class and fields
|
92
|
+
# return that otherwise return the raw format of :attach_to_find_by_field
|
93
|
+
|
94
|
+
def attach_to_field
|
95
|
+
@attach_to_method_detail || @attach_to_field
|
96
|
+
end
|
97
|
+
|
98
|
+
|
89
99
|
# This version creates attachments and also attaches them to instances of :attach_to_klazz
|
90
100
|
#
|
91
101
|
# Options
|
@@ -125,7 +135,7 @@ module DataShift
|
|
125
135
|
|
126
136
|
record = nil
|
127
137
|
|
128
|
-
puts "
|
138
|
+
puts "Attempting to find matching record where #{attach_to_find_by_field} ~= #{base_name}"
|
129
139
|
record = get_record_by(attach_to_klass, attach_to_find_by_field, base_name, split_on)
|
130
140
|
|
131
141
|
if(record)
|
@@ -139,12 +149,11 @@ module DataShift
|
|
139
149
|
|
140
150
|
# Check if attachment must have an associated record
|
141
151
|
if(record)
|
142
|
-
puts "now create attachment"
|
143
152
|
reset()
|
144
153
|
|
145
154
|
create_attachment(@load_object_class, file_name, record, attach_to_field, options)
|
146
155
|
|
147
|
-
puts "Added Attachment #{File.basename(file_name)} to #{record.send(attach_to_find_by_field)} : #{record.id}" if(@verbose)
|
156
|
+
puts "Added Attachment #{File.basename(file_name)} to #{record.send(attach_to_find_by_field)}(id : #{record.id})" if(@verbose)
|
148
157
|
end
|
149
158
|
|
150
159
|
end
|
@@ -153,7 +162,7 @@ module DataShift
|
|
153
162
|
FileUtils.mkdir_p('MissingAttachmentRecords') unless File.directory?('MissingAttachmentRecords')
|
154
163
|
|
155
164
|
puts "WARNING : #{missing_records.size} of #{loading_files_cache.size} files could not be attached to a #{@load_object_class}"
|
156
|
-
puts "For your convenience
|
165
|
+
puts "For your convenience files with MISSING #{attach_to_klass} have been copied to : MissingAttachmentRecords"
|
157
166
|
missing_records.each do |i|
|
158
167
|
puts "Copying #{i} to MissingAttachmentRecords folder" if(options[:verbose])
|
159
168
|
FileUtils.cp( i, 'MissingAttachmentRecords') unless(options[:dummy] == 'true')
|
@@ -56,7 +56,7 @@ module DataShift
|
|
56
56
|
#
|
57
57
|
# Pass through hash of attributes to klass initializer
|
58
58
|
#
|
59
|
-
# :
|
59
|
+
# :has_attached_file_name
|
60
60
|
#
|
61
61
|
# Paperclip attachment name defined with macro 'has_attached_file :name'
|
62
62
|
#
|
@@ -71,7 +71,7 @@ module DataShift
|
|
71
71
|
#
|
72
72
|
def create_attachment(klass, attachment_path, record = nil, attach_to_record_field = nil, options = {})
|
73
73
|
|
74
|
-
has_attached_file_attribute = options[
|
74
|
+
has_attached_file_attribute = options[:has_attached_file_name] ? options[:has_attached_file_name].to_sym : :attachment
|
75
75
|
|
76
76
|
attributes = { has_attached_file_attribute => get_file(attachment_path) }
|
77
77
|
|
@@ -81,6 +81,7 @@ module DataShift
|
|
81
81
|
|
82
82
|
#attributes.merge!(attach_to_record_field.to_sym => record) if(record && attach_to_record_field)
|
83
83
|
|
84
|
+
puts attributes.inspect
|
84
85
|
begin
|
85
86
|
|
86
87
|
@attachment = klass.new(attributes, :without_protection => true)
|
@@ -92,11 +93,12 @@ module DataShift
|
|
92
93
|
attach_to_record_field.assign(record, @attachment)
|
93
94
|
else
|
94
95
|
# assume its not a has_many and try basic send
|
95
|
-
record.send(attach_to_record_field
|
96
|
+
record.send("#{attach_to_record_field}=", @attachment)
|
96
97
|
end if(record && attach_to_record_field)
|
97
98
|
|
98
99
|
else
|
99
100
|
puts "ERROR : Problem saving to DB : #{@attachment.inspect}"
|
101
|
+
puts @attachment.errors.messages.inspect
|
100
102
|
end
|
101
103
|
|
102
104
|
@attachment
|
@@ -14,9 +14,6 @@ module DataShift
|
|
14
14
|
|
15
15
|
include DataShift::Paperclip
|
16
16
|
|
17
|
-
def initialize(attachment_klazz, attachment = nil, options = {})
|
18
|
-
super( attachment_klazz, attachment, options )
|
19
|
-
end
|
20
17
|
|
21
18
|
# Note the paperclip attachment model defines the storage path via something like :
|
22
19
|
#
|
@@ -33,15 +30,17 @@ module DataShift
|
|
33
30
|
# :viewable_record
|
34
31
|
#
|
35
32
|
def create_attachment(klass, attachment_path, record = nil, attach_to_record_field = nil, options = {})
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
|
34
|
+
image_attributes = { :attributes =>
|
35
|
+
{ :alt => (options[:alt] || ""),
|
36
|
+
:position => (!options[:position] && record and record.respond_to?(:images)) ? record.images.length : 0
|
37
|
+
}
|
38
|
+
}
|
42
39
|
|
43
|
-
attachment_options.merge
|
44
|
-
|
40
|
+
attachment_options = options.dup.merge(image_attributes)
|
41
|
+
|
42
|
+
#puts "DEBUG : create_attachment options : #{attachment_options.inspect}"
|
43
|
+
|
45
44
|
super(klass, attachment_path, record, attach_to_record_field, attachment_options)
|
46
45
|
end
|
47
46
|
|
data/lib/thor/import.thor
CHANGED
@@ -63,11 +63,11 @@ module Datashift
|
|
63
63
|
logger.info("INFO: Using loader : #{loader.class}")
|
64
64
|
rescue
|
65
65
|
logger.error("INFO: No specific #{model}Loader found - using generic ExcelLoader")
|
66
|
-
loader = DataShift::ExcelLoader.new(klass)
|
66
|
+
loader = DataShift::ExcelLoader.new(klass, true)
|
67
67
|
end
|
68
68
|
else
|
69
69
|
logger.info("No Loader specified - using generic ExcelLoader")
|
70
|
-
loader = DataShift::ExcelLoader.new(klass)
|
70
|
+
loader = DataShift::ExcelLoader.new(klass, true)
|
71
71
|
end
|
72
72
|
|
73
73
|
logger.info("ARGS #{options.inspect}")
|
data/lib/thor/paperclip.thor
CHANGED
@@ -22,11 +22,32 @@ module Datashift
|
|
22
22
|
|
23
23
|
include DataShift::Logging
|
24
24
|
|
25
|
-
desc "attach", "
|
26
|
-
|
25
|
+
desc "attach", "Create paperclip attachments and attach to a Model from files in a directory.
|
26
|
+
This is specifically for the use case where the paperclip attachments are stored in a class, such as Image, Icon, Asset,
|
27
|
+
and this class has a relationship, such as belongs_to, with another class, such as Product, User, Document.
|
28
|
+
|
29
|
+
Each matching file is used to create an instance of the paperclip attachment, given by :attachment_klass.
|
30
|
+
|
31
|
+
The class with the relationship, can be specified via :attach_to_klass
|
32
|
+
|
27
33
|
Examples
|
28
|
-
Owner has_many pdfs and mp3 files as Digitals .... :attach_to_klass = Owner
|
29
|
-
User has a single
|
34
|
+
Owner has_many pdfs and mp3 files as Digitals .... :attachment_klass = Digital and :attach_to_klass = Owner
|
35
|
+
User has a single Image used as an avatar ... attachment_klass = Image and :attach_to_klass = User
|
36
|
+
|
37
|
+
The file name is used to lookup the instance of :attach_to_klass to assign the new attachment to, via :attach_to_find_by_field
|
38
|
+
|
39
|
+
So say we have a file called smithj_avatar.gif, and we want to lookup Users by login
|
40
|
+
|
41
|
+
:attach_to_find_by_field = login => Run a loookup based on find_by_login == 'smithj'
|
42
|
+
|
43
|
+
Once instance of :attach_to_klass found, the new attachment is assigned.
|
44
|
+
|
45
|
+
The attribute to assign new attachment to is gtiven by :attach_to_field
|
46
|
+
|
47
|
+
Examples
|
48
|
+
:attach_to_field => digitals : Owner.digitals = attachment(Digital)
|
49
|
+
:attach_to_field => avatar : User.avatar = attachment(Image)"
|
50
|
+
|
30
51
|
|
31
52
|
# :dummy => dummy run without actual saving to DB
|
32
53
|
method_option :input, :aliases => '-i', :required => true, :desc => "The input path containing images "
|
@@ -35,13 +56,11 @@ module Datashift
|
|
35
56
|
method_option :recursive, :aliases => '-r', :type => :boolean, :desc => "Scan sub directories of input for images"
|
36
57
|
|
37
58
|
method_option :attachment_klass, :required => true, :aliases => '-a', :desc => "Ruby Class name of the Attachment e.g Image, Icon"
|
38
|
-
method_option :attach_to_klass, :required => true, :aliases => '-k', :desc => "A class that has a relationship with the attachment (has_many, has_one, belongs_to)"
|
39
|
-
|
40
|
-
|
41
|
-
method_option :attach_to_field, :required => true, :aliases => '-f', :desc => "Attachment belongs to field e.g Product.image, Blog.digital"
|
42
59
|
|
60
|
+
method_option :attach_to_klass, :required => true, :aliases => '-k', :desc => "A class that has a relationship with the attachment (has_many, has_one, belongs_to)"
|
43
61
|
method_option :attach_to_find_by_field, :required => true, :aliases => '-l', :desc => "The field to use to find the :attach_to_klass record"
|
44
|
-
|
62
|
+
method_option :attach_to_field, :required => true, :aliases => '-f', :desc => "Attachment belongs to field e.g Product.image, Blog.digital"
|
63
|
+
|
45
64
|
|
46
65
|
# => :attach_to_find_by_field
|
47
66
|
# For the :attach_to_klass, this is the field used to search for the parent
|
data/spec/excel_loader_spec.rb
CHANGED
@@ -31,9 +31,6 @@ describe 'Excel Loader' do
|
|
31
31
|
Category.find_or_create_by_reference(cat)
|
32
32
|
end
|
33
33
|
|
34
|
-
DataShift::MethodDictionary.find_operators( Category )
|
35
|
-
|
36
|
-
DataShift::MethodDictionary.build_method_details( Category )
|
37
34
|
|
38
35
|
end
|
39
36
|
|
@@ -57,6 +54,11 @@ describe 'Excel Loader' do
|
|
57
54
|
|
58
55
|
it "should process multiple associationss from single column" do
|
59
56
|
|
57
|
+
|
58
|
+
DataShift::MethodDictionary.find_operators( Category )
|
59
|
+
|
60
|
+
DataShift::MethodDictionary.build_method_details( Category )
|
61
|
+
|
60
62
|
Project.find_by_title('001').should be_nil
|
61
63
|
count = Project.count
|
62
64
|
|
Binary file
|
@@ -8,25 +8,34 @@
|
|
8
8
|
# and a classes different types of assignment operators
|
9
9
|
#
|
10
10
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
11
|
-
|
11
|
+
|
12
|
+
require 'method_dictionary'
|
13
|
+
|
12
14
|
describe 'Method Dictionary' do
|
13
15
|
|
16
|
+
|
14
17
|
include_context "ActiveRecordTestModelsConnected"
|
15
18
|
|
19
|
+
include DataShift
|
16
20
|
|
17
21
|
before(:each) do
|
18
22
|
MethodDictionary.clear
|
19
|
-
MethodDictionary.find_operators( Project )
|
20
|
-
MethodDictionary.find_operators( Milestone )
|
21
23
|
end
|
22
24
|
|
23
25
|
it "should store dictionary for multiple AR models" do
|
26
|
+
|
27
|
+
MethodDictionary.find_operators( Project )
|
28
|
+
MethodDictionary.find_operators( Milestone )
|
29
|
+
|
24
30
|
MethodDictionary.assignments.size.should == 2
|
25
31
|
MethodDictionary.has_many.size.should == 2
|
26
32
|
end
|
27
33
|
|
28
34
|
it "should populate method dictionary for a given AR model" do
|
29
35
|
|
36
|
+
MethodDictionary.find_operators( Project )
|
37
|
+
MethodDictionary.find_operators( Milestone )
|
38
|
+
|
30
39
|
MethodDictionary.has_many.should_not be_empty
|
31
40
|
MethodDictionary.has_many[Project].should include('milestones')
|
32
41
|
|
@@ -48,6 +57,8 @@ describe 'Method Dictionary' do
|
|
48
57
|
|
49
58
|
it "should populate assigment members without the equivalent association names" do
|
50
59
|
|
60
|
+
MethodDictionary.find_operators( Project )
|
61
|
+
|
51
62
|
# we should remove has-many & belongs_to from basic assignment set as they require a DB lookup
|
52
63
|
# or a Model.create call, not a simple assignment
|
53
64
|
|
@@ -58,6 +69,9 @@ describe 'Method Dictionary' do
|
|
58
69
|
|
59
70
|
it "should populate assignment operators for method details for different forms of a column name" do
|
60
71
|
|
72
|
+
MethodDictionary.find_operators( Project )
|
73
|
+
MethodDictionary.find_operators( Milestone )
|
74
|
+
|
61
75
|
MethodDictionary.build_method_details( Project )
|
62
76
|
|
63
77
|
[:value_as_string, 'value_as_string', "VALUE as_STRING", "value as string"].each do |format|
|
@@ -83,6 +97,8 @@ describe 'Method Dictionary' do
|
|
83
97
|
|
84
98
|
it "should populate column types for assignment operators in method details" do
|
85
99
|
|
100
|
+
MethodDictionary.find_operators( Project )
|
101
|
+
|
86
102
|
MethodDictionary.build_method_details( Project )
|
87
103
|
|
88
104
|
[:value_as_string, 'value_as_string', "VALUE as_STRING", "value as string"].each do |format|
|
@@ -101,6 +117,8 @@ describe 'Method Dictionary' do
|
|
101
117
|
|
102
118
|
it "should populate required Class for assignment operators based on column type" do
|
103
119
|
|
120
|
+
MethodDictionary.find_operators( Project )
|
121
|
+
|
104
122
|
MethodDictionary.build_method_details( Project )
|
105
123
|
|
106
124
|
[:value_as_string, 'value_as_string', "VALUE as_STRING", "value as string"].each do |format|
|
@@ -116,6 +134,9 @@ describe 'Method Dictionary' do
|
|
116
134
|
|
117
135
|
it "should populate belongs_to operator for method details for different forms of a column name" do
|
118
136
|
|
137
|
+
MethodDictionary.find_operators( Project )
|
138
|
+
MethodDictionary.find_operators( Milestone )
|
139
|
+
|
119
140
|
MethodDictionary.build_method_details( Project )
|
120
141
|
MethodDictionary.build_method_details( Milestone )
|
121
142
|
|
@@ -184,10 +205,24 @@ describe 'Method Dictionary' do
|
|
184
205
|
method_details.operator_class.should == LongAndComplexTableLinkedToVersion
|
185
206
|
end
|
186
207
|
end
|
208
|
+
|
209
|
+
it "should return false on for?(klass) if find_operators hasn't been called for klass" do
|
210
|
+
MethodDictionary.clear
|
211
|
+
MethodDictionary::for?(Project).should == false
|
212
|
+
MethodDictionary::find_operators(Project)
|
213
|
+
MethodDictionary::for?(Project).should == true
|
214
|
+
|
215
|
+
end
|
187
216
|
|
188
|
-
|
217
|
+
it "should return false on for?(klass) if find_operators hasn't been called for klass" do
|
218
|
+
MethodDictionary.clear
|
219
|
+
MethodDictionary::for?(Project).should == false
|
220
|
+
end
|
221
|
+
|
189
222
|
it "should find has_many operator for method details" do
|
190
|
-
|
223
|
+
|
224
|
+
MethodDictionary.find_operators( Project )
|
225
|
+
|
191
226
|
MethodDictionary.build_method_details( Project )
|
192
227
|
|
193
228
|
[:milestones, "Mile Stones", 'mileSTONES', 'MileStones'].each do |format|
|
@@ -208,6 +243,9 @@ describe 'Method Dictionary' do
|
|
208
243
|
|
209
244
|
|
210
245
|
it "should return nil when non existent column name" do
|
246
|
+
|
247
|
+
MethodDictionary.find_operators( Project )
|
248
|
+
|
211
249
|
["On sale", 'on_sale'].each do |format|
|
212
250
|
detail = MethodDictionary.find_method_detail( Project, format )
|
213
251
|
|
@@ -221,11 +259,16 @@ describe 'Method Dictionary' do
|
|
221
259
|
end
|
222
260
|
|
223
261
|
it "should not by default map setter methods" do
|
262
|
+
MethodDictionary.find_operators( Milestone )
|
263
|
+
|
224
264
|
MethodDictionary.assignments[Milestone].should_not include('title')
|
225
265
|
end
|
226
266
|
|
227
267
|
it "should support reload and inclusion of setter methods" do
|
228
268
|
|
269
|
+
MethodDictionary.find_operators( Project )
|
270
|
+
MethodDictionary.find_operators( Milestone )
|
271
|
+
|
229
272
|
MethodDictionary.assignments[Milestone].should_not include('title')
|
230
273
|
|
231
274
|
MethodDictionary.find_operators( Milestone, :reload => true, :instance_methods => true )
|
@@ -38,7 +38,7 @@ describe 'PaperClip Bulk Loader' do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should create a new paperclip loader and define attachment class" do
|
41
|
-
loader = DataShift::Paperclip::AttachmentLoader.new(@attachment_klass, nil, @common_options)
|
41
|
+
loader = DataShift::Paperclip::AttachmentLoader.new(@attachment_klass, true, nil, @common_options)
|
42
42
|
|
43
43
|
loader.load_object_class.should == Digital
|
44
44
|
loader.load_object.should be_a Digital
|
@@ -50,7 +50,7 @@ describe 'PaperClip Bulk Loader' do
|
|
50
50
|
|
51
51
|
opts = { :attach_to_klass => Owner }.merge(@common_options)
|
52
52
|
|
53
|
-
loader = DataShift::Paperclip::AttachmentLoader.new(@attachment_klass, nil, opts)
|
53
|
+
loader = DataShift::Paperclip::AttachmentLoader.new(@attachment_klass, true, nil, opts)
|
54
54
|
|
55
55
|
loader.attach_to_klass.should == Owner
|
56
56
|
end
|
metadata
CHANGED
@@ -1,58 +1,50 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: datashift
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.2
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.11.0
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Thomas Statter
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
|
13
|
+
date: 2012-10-23 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
15
16
|
name: spreadsheet
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version:
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
30
|
-
- !ruby/object:Gem::Dependency
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
31
27
|
name: rubyzip
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
28
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
33
29
|
none: false
|
34
|
-
requirements:
|
35
|
-
- -
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version:
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
|
-
version_requirements:
|
41
|
-
|
42
|
-
requirements:
|
43
|
-
- - ! '>='
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: '0'
|
46
|
-
description: Comprehensive tools to import/export between Excel/CSV and ActiveRecord
|
47
|
-
databases, Rails apps, and any Ruby project.
|
36
|
+
version_requirements: *id002
|
37
|
+
description: Comprehensive tools to import/export between Excel/CSV and ActiveRecord databases, Rails apps, and any Ruby project.
|
48
38
|
email: rubygems@autotelik.co.uk
|
49
39
|
executables: []
|
40
|
+
|
50
41
|
extensions: []
|
51
|
-
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
52
44
|
- LICENSE.txt
|
53
45
|
- README.markdown
|
54
46
|
- README.rdoc
|
55
|
-
files:
|
47
|
+
files:
|
56
48
|
- .document
|
57
49
|
- LICENSE.txt
|
58
50
|
- README.markdown
|
@@ -229,28 +221,31 @@ files:
|
|
229
221
|
- tasks/file_tasks.rake
|
230
222
|
- tasks/word_to_seedfu.rake
|
231
223
|
homepage: http://github.com/autotelik/datashift
|
232
|
-
licenses:
|
224
|
+
licenses:
|
233
225
|
- MIT
|
234
226
|
post_install_message:
|
235
227
|
rdoc_options: []
|
236
|
-
|
228
|
+
|
229
|
+
require_paths:
|
237
230
|
- lib
|
238
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
231
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
239
232
|
none: false
|
240
|
-
requirements:
|
241
|
-
- -
|
242
|
-
- !ruby/object:Gem::Version
|
243
|
-
version:
|
244
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - ">="
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: "0"
|
237
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
245
238
|
none: false
|
246
|
-
requirements:
|
247
|
-
- -
|
248
|
-
- !ruby/object:Gem::Version
|
249
|
-
version:
|
239
|
+
requirements:
|
240
|
+
- - ">="
|
241
|
+
- !ruby/object:Gem::Version
|
242
|
+
version: "0"
|
250
243
|
requirements: []
|
244
|
+
|
251
245
|
rubyforge_project:
|
252
246
|
rubygems_version: 1.8.24
|
253
247
|
signing_key:
|
254
248
|
specification_version: 3
|
255
249
|
summary: Shift data betwen Excel/CSV and any Ruby app
|
256
250
|
test_files: []
|
251
|
+
|