datashift 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +36 -66
- data/VERSION +1 -1
- data/lib/applications/jexcel_file.rb +12 -5
- data/lib/datashift.rb +18 -13
- data/lib/datashift/delimiters.rb +0 -1
- data/lib/{guards.rb → datashift/guards.rb} +0 -0
- data/lib/datashift/method_detail.rb +4 -67
- data/lib/datashift/method_details_manager.rb +18 -6
- data/lib/datashift/method_dictionary.rb +55 -38
- data/lib/datashift/method_mapper.rb +18 -14
- data/lib/datashift/populator.rb +259 -6
- data/lib/exporters/csv_exporter.rb +28 -19
- data/lib/exporters/excel_exporter.rb +18 -5
- data/lib/generators/excel_generator.rb +2 -0
- data/lib/loaders/excel_loader.rb +2 -1
- data/lib/loaders/loader_base.rb +53 -142
- data/lib/loaders/paperclip/attachment_loader.rb +1 -1
- data/lib/loaders/paperclip/datashift_paperclip.rb +51 -44
- data/lib/thor/export.thor +65 -0
- data/lib/thor/generate.thor +68 -4
- data/spec/Gemfile +12 -8
- data/spec/Gemfile.lock +93 -93
- data/spec/csv_exporter_spec.rb +50 -12
- data/spec/excel_exporter_spec.rb +35 -3
- data/spec/excel_loader_spec.rb +9 -7
- data/spec/excel_spec.rb +26 -5
- data/spec/{loader_spec.rb → loader_base_spec.rb} +13 -1
- data/spec/method_dictionary_spec.rb +77 -70
- data/spec/paperclip_loader_spec.rb +1 -1
- data/spec/populator_spec.rb +94 -0
- data/spec/thor_spec.rb +1 -1
- metadata +70 -68
data/README.markdown
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
## DataShift
|
2
2
|
|
3
|
+
- [Features](#features)
|
4
|
+
- [Installation](#installation)
|
5
|
+
- [Active Record - Import/Export](#Active Record - Import/Export)
|
6
|
+
- [License](#license)
|
7
|
+
|
3
8
|
Provides tools to shift data between Excel/CSV files and Rails projects and Ruby applications
|
4
9
|
|
5
10
|
Import and export models fully with all associations.
|
@@ -7,7 +12,8 @@ Import and export models fully with all associations.
|
|
7
12
|
Comprehensive Wiki here : **https://github.com/autotelik/datashift/wiki**
|
8
13
|
|
9
14
|
Specific command line tools and full Product loading for Spree E-Commerce
|
10
|
-
now
|
15
|
+
now separate gem at [datashift_spree](https://github.com/autotelik/datashift_spree "Datashift Spree")
|
16
|
+
|
11
17
|
|
12
18
|
### Features
|
13
19
|
|
@@ -40,11 +46,15 @@ Many example Spreadsheets/CSV files in spec/fixtures, fully documented with comm
|
|
40
46
|
|
41
47
|
Add gem 'datashift' to your Gemfile/bundle or use ```gem install```
|
42
48
|
|
43
|
-
|
49
|
+
```ruby
|
50
|
+
gem 'datashift'
|
51
|
+
```
|
44
52
|
|
45
53
|
For Spree support also add :
|
46
54
|
|
47
|
-
|
55
|
+
```ruby
|
56
|
+
gem 'datashift_spree'
|
57
|
+
```
|
48
58
|
|
49
59
|
To use :
|
50
60
|
|
@@ -106,6 +116,26 @@ Provides high level tasks for exporting data to various sources, currently .xls
|
|
106
116
|
|
107
117
|
bundle exec thor datashift:export:excel model=BlogPost result=BlogExport.xls
|
108
118
|
|
119
|
+
|
120
|
+
Import based on column headings with *Semi-Smart Name Lookup*
|
121
|
+
|
122
|
+
On import, first a dictionary of all possible attributes and associations is created for the AR class.
|
123
|
+
|
124
|
+
This enables lookup, of a user supplied name (column heading), managing white space, pluralisation etc .
|
125
|
+
|
126
|
+
Example usage, load from a file or spreadsheet where the column names are only
|
127
|
+
an approximation of the actual associations, so given 'Product Properties' heading,
|
128
|
+
finds real association 'product_properties' to send or call on the AR object
|
129
|
+
|
130
|
+
|
131
|
+
Can import/export 'belongs_to, 'has_many' and 'has_one' associations, including assignment of multiple objects
|
132
|
+
via either multiple columns, or via a DSL for creating multiple entries in a single (column).
|
133
|
+
|
134
|
+
The DSL can also be used to define which fields to lookup associations, and assign values to other fields.
|
135
|
+
|
136
|
+
See Wiki for more details on DSL syntax.
|
137
|
+
|
138
|
+
Supports inclusion of delegated attributes and normal instance methods as column headings.
|
109
139
|
|
110
140
|
The library can be easily extended with Loaders to deal with non trivial cases,
|
111
141
|
for example when multiple lookups required to find right association.
|
@@ -135,22 +165,8 @@ This data can be exported directly to CSV or Excel/OpenOffice spreadsheets.
|
|
135
165
|
Column headings contain comments with full descriptions and instructions on syntax.
|
136
166
|
|
137
167
|
|
138
|
-
##
|
139
|
-
|
140
|
-
- *Associations*
|
141
|
-
|
142
|
-
Can import/export 'belongs_to, 'has_many' and 'has_one' associations, including assignment of multiple objects
|
143
|
-
via either multiple columns, or via a DSL for creating multiple entries in a single (column).
|
168
|
+
## Excel
|
144
169
|
|
145
|
-
The DSL can also be used to define which fields to lookup associations, and assign values to other fields.
|
146
|
-
|
147
|
-
See Wiki for more details on DSL syntax.
|
148
|
-
|
149
|
-
Supports delegated attributes.
|
150
|
-
|
151
|
-
- *High level wrappers around applications including Excel and Word
|
152
|
-
|
153
|
-
Quickly and easily access common enterprise applications through Ruby
|
154
170
|
|
155
171
|
MS Excel itself does not need to be installed.
|
156
172
|
|
@@ -160,55 +176,10 @@ This data can be exported directly to CSV or Excel/OpenOffice spreadsheets.
|
|
160
176
|
|
161
177
|
The required POI jars are already included.
|
162
178
|
|
163
|
-
- *Direct Excel export*
|
164
|
-
|
165
179
|
Excel/OpenOffice spreadsheets are heavily used in many sectors, so direct support makes it
|
166
|
-
easier and quicker to migrate your client's data into a Rails/ActiveRecord project
|
167
|
-
|
168
|
-
No need to save to CSV or map to YAML.
|
169
|
-
|
170
|
-
- *Semi-Smart Name Lookup*
|
171
|
-
|
172
|
-
Includes helper classes that find and store details of all possible associations on an AR class.
|
173
|
-
Given a user supplied name, attempts to find the requested association.
|
174
|
-
|
175
|
-
Example usage, load from a file or spreadsheet where the column names are only
|
176
|
-
an approximation of the actual associations, so given 'Product Properties' heading,
|
177
|
-
finds real association 'product_properties' to send or call on the AR object
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
- *Thor Tasks*
|
182
|
-
|
183
|
-
High level Thor CLIs are provided, only required to supply model class, and file location :
|
184
|
-
|
185
|
-
thor datashift:import:excel model=MusicTrack input=MyTrackListing.xls
|
186
|
-
|
187
|
-
|
188
|
-
- *Spree Tasks*
|
189
|
-
|
190
|
-
Spree's product associations are non trivial so specific Rake tasks are also provided for loading Spree Producta
|
191
|
-
with all associations and Image loading.
|
192
|
-
|
193
|
-
thor datashift:spree:products input=C:\MyProducts.xls
|
194
|
-
|
195
|
-
|
196
|
-
- *Seamless Spree Image loading can be achieved by ensuring SKU or class Name features in Image filename.
|
197
|
-
|
198
|
-
Lookup is performed either via the SKU being prepended to the image name, or by the image name being equal to the **name attribute** of the klass in question.
|
199
|
-
|
200
|
-
Images can be attached to any class defined with a suitable association. The class to use can be configured in rake task via
|
201
|
-
parameter klass=Xyz.
|
202
|
-
|
203
|
-
In the Spree tasks, this defaults to Product, so attempts to attach Image to a Product via Product SKU or Name.
|
204
|
-
|
205
|
-
A report is generated in the current working directory detailing any Images in the paths that could not be matched with a Product.
|
206
|
-
|
207
|
-
thor datashift:spree:images input=C:\images\product_images skip_if_no_assoc=true
|
208
|
-
|
209
|
-
thor datashift:spree:images input=C:\images\taxon_icons skip_if_no_assoc=true klass=Taxon
|
180
|
+
easier and quicker to migrate your client's data into a Rails/ActiveRecord project,
|
181
|
+
without converting first to CSV or YAML.
|
210
182
|
|
211
|
-
## Import to Active Record
|
212
183
|
|
213
184
|
### Associations
|
214
185
|
|
@@ -237,7 +208,6 @@ During loading, a call to find_all_by_reference will be made, picking up the 2 c
|
|
237
208
|
- Look at implementing import/export API using something like https://github.com/ianwhite/orm_adapter
|
238
209
|
rather than active record, so we can support additional ORMs
|
239
210
|
|
240
|
-
- Create separate Spree extension to support import/export via the admin gui
|
241
211
|
|
242
212
|
## License
|
243
213
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.14.0
|
@@ -95,10 +95,14 @@ if(DataShift::Guards::jruby?)
|
|
95
95
|
|
96
96
|
return create_sheet_and_set_styles( sheet_name )
|
97
97
|
else
|
98
|
-
|
99
|
-
|
98
|
+
|
99
|
+
name = sanitize_sheet_name( sheet_name )
|
100
|
+
|
101
|
+
puts "WTF #{name}"
|
102
|
+
if (@workbook.getSheetIndex(name) < 0) #Check sheet doesn't already exist
|
103
|
+
return create_sheet_and_set_styles( name )
|
100
104
|
else
|
101
|
-
activate_sheet(
|
105
|
+
activate_sheet(name)
|
102
106
|
end
|
103
107
|
end
|
104
108
|
end
|
@@ -205,9 +209,12 @@ if(DataShift::Guards::jruby?)
|
|
205
209
|
private
|
206
210
|
|
207
211
|
def create_sheet_and_set_styles( sheet_name )
|
208
|
-
|
212
|
+
|
213
|
+
name = sanitize_sheet_name(sheet_name)
|
214
|
+
|
215
|
+
@sheet = @workbook.createSheet( name )
|
209
216
|
|
210
|
-
@patriarchs.store(
|
217
|
+
@patriarchs.store(name, @sheet.createDrawingPatriarch())
|
211
218
|
|
212
219
|
@date_style = @workbook.createCellStyle
|
213
220
|
@date_style.setDataFormat( JExcelFile::date_format )
|
data/lib/datashift.rb
CHANGED
@@ -32,21 +32,9 @@
|
|
32
32
|
#
|
33
33
|
# DataShift::load_commands
|
34
34
|
#
|
35
|
-
require 'rbconfig'
|
36
|
-
require 'guards'
|
37
35
|
|
38
36
|
module DataShift
|
39
37
|
|
40
|
-
if(Guards::jruby?)
|
41
|
-
require 'java'
|
42
|
-
|
43
|
-
class Object
|
44
|
-
def add_to_classpath(path)
|
45
|
-
$CLASSPATH << File.join( DataShift.root_path, 'lib', path.gsub("\\", "/") )
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
38
|
def self.gem_version
|
51
39
|
unless(@gem_version)
|
52
40
|
if(File.exists?('VERSION'))
|
@@ -126,4 +114,21 @@ module DataShift
|
|
126
114
|
|
127
115
|
end
|
128
116
|
|
129
|
-
DataShift::require_libraries
|
117
|
+
DataShift::require_libraries
|
118
|
+
|
119
|
+
require 'datashift/guards'
|
120
|
+
require 'datashift/method_detail'
|
121
|
+
require 'datashift/method_dictionary'
|
122
|
+
require 'datashift/method_mapper'
|
123
|
+
|
124
|
+
module DataShift
|
125
|
+
if(Guards::jruby?)
|
126
|
+
require 'java'
|
127
|
+
|
128
|
+
class Object
|
129
|
+
def add_to_classpath(path)
|
130
|
+
$CLASSPATH << File.join( DataShift.root_path, 'lib', path.gsub("\\", "/") )
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/datashift/delimiters.rb
CHANGED
@@ -17,7 +17,6 @@ module DataShift
|
|
17
17
|
# Support multiple associations being added to a base object to be specified in a single column.
|
18
18
|
#
|
19
19
|
# Entry represents the association to find via supplied name, value to use in the lookup.
|
20
|
-
# Can contain multiple lookup name/value pairs, separated by multi_assoc_delim ( | )
|
21
20
|
#
|
22
21
|
# Default syntax :
|
23
22
|
#
|
File without changes
|
@@ -20,10 +20,7 @@ module DataShift
|
|
20
20
|
class MethodDetail
|
21
21
|
|
22
22
|
include DataShift::Logging
|
23
|
-
|
24
|
-
include DataShift::Populator
|
25
|
-
extend DataShift::Populator
|
26
|
-
|
23
|
+
|
27
24
|
def self.supported_types_enum
|
28
25
|
@type_enum ||= Set[:assignment, :belongs_to, :has_one, :has_many]
|
29
26
|
@type_enum
|
@@ -33,10 +30,7 @@ module DataShift
|
|
33
30
|
@assoc_type_enum ||= Set[:belongs_to, :has_one, :has_many]
|
34
31
|
@assoc_type_enum
|
35
32
|
end
|
36
|
-
|
37
|
-
# When looking up an association, try each of these in turn till a match
|
38
|
-
# i.e find_by_name .. find_by_title and so on, lastly try the raw id
|
39
|
-
@@insistent_find_by_list ||= [:name, :title, :id]
|
33
|
+
|
40
34
|
|
41
35
|
# Name is the raw, client supplied name
|
42
36
|
attr_accessor :name
|
@@ -115,60 +109,6 @@ module DataShift
|
|
115
109
|
@operator_class
|
116
110
|
end
|
117
111
|
|
118
|
-
def assign(record, value )
|
119
|
-
|
120
|
-
@current_value = value
|
121
|
-
|
122
|
-
# logger.info("WARNING nil value supplied for Column [#{@name}]") if(@current_value.nil?)
|
123
|
-
|
124
|
-
if( operator_for(:belongs_to) )
|
125
|
-
|
126
|
-
#puts "DEBUG : BELONGS_TO : #{@name} : #{operator} - Lookup #{@current_value} in DB"
|
127
|
-
insistent_belongs_to(record, @current_value)
|
128
|
-
|
129
|
-
elsif( operator_for(:has_many) )
|
130
|
-
|
131
|
-
#puts "DEBUG : VALUE TYPE [#{value.class.name.include?(operator.classify)}] [#{ModelMapper.class_from_string(value.class.name)}]" unless(value.is_a?(Array))
|
132
|
-
|
133
|
-
# The include? check is best I can come up with right now .. to handle module/namespaces
|
134
|
-
# TODO - can we determine the real class type of an association
|
135
|
-
# e.g given a association taxons, which operator.classify gives us Taxon, but actually it's Spree::Taxon
|
136
|
-
# so how do we get from 'taxons' to Spree::Taxons ? .. check if further info in reflect_on_all_associations
|
137
|
-
|
138
|
-
if(value.is_a?(Array) || value.class.name.include?(operator.classify))
|
139
|
-
record.send(operator) << value
|
140
|
-
else
|
141
|
-
puts "ERROR #{value.class} - Not expected type for has_many #{operator} - cannot assign"
|
142
|
-
end
|
143
|
-
|
144
|
-
elsif( operator_for(:has_one) )
|
145
|
-
|
146
|
-
#puts "DEBUG : HAS_MANY : #{@name} : #{operator}(#{operator_class}) - Lookup #{@current_value} in DB"
|
147
|
-
if(value.is_a?(operator_class))
|
148
|
-
record.send(operator + '=', value)
|
149
|
-
else
|
150
|
-
logger.error("ERROR #{value.class} - Not expected type for has_one #{operator} - cannot assign")
|
151
|
-
# TODO - Not expected type - maybe try to look it up somehow ?"
|
152
|
-
#insistent_has_many(record, @current_value)
|
153
|
-
end
|
154
|
-
|
155
|
-
elsif( operator_for(:assignment) && @col_type )
|
156
|
-
#puts "DEBUG : COl TYPE defined for #{@name} : #{@assignment} => #{@current_value} #{@col_type.type}"
|
157
|
-
# puts "DEBUG : Column [#{@name}] : COl TYPE CAST: #{@current_value} => #{@col_type.type_cast( @current_value ).inspect}"
|
158
|
-
record.send( operator + '=' , @col_type.type_cast( @current_value ) )
|
159
|
-
|
160
|
-
#puts "DEBUG : MethodDetails Assignment RESULT: #{record.send(operator)}"
|
161
|
-
|
162
|
-
elsif( operator_for(:assignment) )
|
163
|
-
#puts "DEBUG : Column [#{@name}] : Brute force assignment of value #{@current_value}"
|
164
|
-
# brute force case for assignments without a column type (which enables us to do correct type_cast)
|
165
|
-
# so in this case, attempt straightforward assignment then if that fails, basic ops such as to_s, to_i, to_f etc
|
166
|
-
insistent_assignment(record, @current_value)
|
167
|
-
else
|
168
|
-
puts "WARNING: No operator found for assignment on #{self.inspect} for Column [#{@name}]"
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
112
|
def pp
|
173
113
|
"#{@name} => #{operator}"
|
174
114
|
end
|
@@ -222,12 +162,9 @@ module DataShift
|
|
222
162
|
end
|
223
163
|
end
|
224
164
|
end
|
225
|
-
|
226
|
-
def insistent_assignment( record, value )
|
227
|
-
Populator::insistent_assignment( record, value, operator)
|
228
|
-
end
|
229
165
|
|
230
|
-
|
166
|
+
private
|
167
|
+
|
231
168
|
# Return the operator's expected class, if can be derived, else nil
|
232
169
|
def get_operator_class()
|
233
170
|
if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
|
@@ -23,10 +23,15 @@ module DataShift
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def add(method_details)
|
26
|
+
#puts "DEBUG: MGR - Add {#method_details.operator_type}\n#{method_details.inspect}"
|
26
27
|
@method_details[method_details.operator_type.to_sym] ||= {}
|
28
|
+
|
29
|
+
# Mapped by Type and MethodDetail name
|
30
|
+
@method_details[method_details.operator_type.to_sym][method_details.name] = method_details
|
31
|
+
|
32
|
+
# Helper list of all available by type
|
27
33
|
@method_details_list[method_details.operator_type.to_sym] ||= []
|
28
34
|
|
29
|
-
@method_details[method_details.operator_type.to_sym][method_details.name] = method_details
|
30
35
|
@method_details_list[method_details.operator_type.to_sym] << method_details
|
31
36
|
@method_details_list[method_details.operator_type.to_sym].uniq!
|
32
37
|
end
|
@@ -38,10 +43,11 @@ module DataShift
|
|
38
43
|
def find(name, type)
|
39
44
|
method_details = get(type)
|
40
45
|
|
41
|
-
method_details ?
|
46
|
+
method_details ? method_details[name] : nil
|
42
47
|
end
|
43
48
|
|
44
|
-
# type is expected to be one of MethodDetail::
|
49
|
+
# type is expected to be one of MethodDetail::supported_types_enum
|
50
|
+
# Returns all MethodDetail(s) for supplied type
|
45
51
|
def get( type )
|
46
52
|
@method_details[type.to_sym]
|
47
53
|
end
|
@@ -51,9 +57,15 @@ module DataShift
|
|
51
57
|
end
|
52
58
|
|
53
59
|
alias_method(:get_list_of_method_details, :get_list)
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
|
61
|
+
# Get list of the inbound or externally supplied names
|
62
|
+
def get_names(type)
|
63
|
+
get_list(type).collect { |md| md.name }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get list of Rails model operators
|
67
|
+
def get_operators(type)
|
68
|
+
get_list(type).collect { |md| md.operator }
|
57
69
|
end
|
58
70
|
|
59
71
|
alias_method(:get_list_of_operators, :get_list)
|
@@ -5,8 +5,6 @@
|
|
5
5
|
#
|
6
6
|
# Details:: A cache type class that stores details of all possible associations on AR classes.
|
7
7
|
#
|
8
|
-
require 'method_detail'
|
9
|
-
|
10
8
|
module DataShift
|
11
9
|
|
12
10
|
class MethodDictionary
|
@@ -23,7 +21,7 @@ module DataShift
|
|
23
21
|
# grouped by type of association (includes belongs_to and has_many which provides both << and = )
|
24
22
|
# Options:
|
25
23
|
# :reload => clear caches and re-perform lookup
|
26
|
-
# :instance_methods => if true include instance method type
|
24
|
+
# :instance_methods => if true include instance method type 'setters' as well as model's pure columns
|
27
25
|
#
|
28
26
|
def self.find_operators(klass, options = {} )
|
29
27
|
|
@@ -34,50 +32,39 @@ module DataShift
|
|
34
32
|
has_many[klass] = klass.reflect_on_all_associations(:has_many).map { |i| i.name.to_s }
|
35
33
|
klass.reflect_on_all_associations(:has_and_belongs_to_many).inject(has_many[klass]) { |x,i| x << i.name.to_s }
|
36
34
|
end
|
37
|
-
|
38
|
-
# puts "DEBUG: Has Many Associations:", has_many[klass].inspect
|
39
35
|
|
40
36
|
# Find the belongs_to associations which can be populated via Model.belongs_to_name = OtherArModelObject
|
41
37
|
if( options[:reload] || belongs_to[klass].nil? )
|
42
38
|
belongs_to[klass] = klass.reflect_on_all_associations(:belongs_to).map { |i| i.name.to_s }
|
43
39
|
end
|
44
40
|
|
45
|
-
#puts "Belongs To Associations:", belongs_to[klass].inspect
|
46
|
-
|
47
41
|
# Find the has_one associations which can be populated via Model.has_one_name = OtherArModelObject
|
48
42
|
if( options[:reload] || has_one[klass].nil? )
|
49
43
|
has_one[klass] = klass.reflect_on_all_associations(:has_one).map { |i| i.name.to_s }
|
50
44
|
end
|
51
45
|
|
52
|
-
#puts "has_one Associations:", self.has_one[klass].inspect
|
53
|
-
|
54
46
|
# Find the model's column associations which can be populated via xxxxxx= value
|
55
47
|
# Note, not all reflections return method names in same style so we convert all to
|
56
48
|
# the raw form i.e without the '=' for consistency
|
57
49
|
if( options[:reload] || assignments[klass].nil? )
|
58
50
|
|
59
|
-
# TODO investigate difference with attribute_names - maybe column names can be assigned to an attribute
|
60
|
-
# so in terms of method calls on klass attribute_names might be safer
|
61
51
|
assignments[klass] = klass.column_names
|
52
|
+
|
53
|
+
# get into consistent format with other assignments names i.e remove the = for now
|
54
|
+
assignments[klass] += setters(klass).map{|i| i.gsub(/=/, '')} if(options[:instance_methods])
|
62
55
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
# TODO - Since 3.2 this seems to return lots more stuff including validations which might not be appropriate
|
67
|
-
if(klass.respond_to? :defined_activerecord_methods)
|
68
|
-
setters = setters - klass.defined_activerecord_methods.to_a
|
69
|
-
end
|
70
|
-
|
71
|
-
# get into same format as other names
|
72
|
-
assignments[klass] += setters.map{|i| i.gsub(/=/, '')}
|
73
|
-
end
|
74
|
-
|
75
|
-
assignments[klass] -= has_many[klass] if(has_many[klass])
|
56
|
+
# Now remove all the associations
|
57
|
+
assignments[klass] -= has_many[klass] if(has_many[klass])
|
76
58
|
assignments[klass] -= belongs_to[klass] if(belongs_to[klass])
|
77
|
-
assignments[klass] -=
|
78
|
-
|
59
|
+
assignments[klass] -= has_one[klass] if(has_one[klass])
|
60
|
+
|
61
|
+
# TODO remove assignments with id
|
62
|
+
# assignments => tax_id but already in belongs_to => tax
|
63
|
+
|
79
64
|
assignments[klass].uniq!
|
80
65
|
|
66
|
+
#puts "\nDEBUG: DICT Setters\n#{assignments[klass]}\n"
|
67
|
+
|
81
68
|
assignments[klass].each do |assign|
|
82
69
|
column_types[klass] ||= {}
|
83
70
|
column_def = klass.columns.find{ |col| col.name == assign }
|
@@ -86,6 +73,18 @@ module DataShift
|
|
86
73
|
end
|
87
74
|
end
|
88
75
|
|
76
|
+
def self.setters( klass )
|
77
|
+
|
78
|
+
# N.B In 1.8 these return strings, in 1.9 symbols.
|
79
|
+
# map everything to strings a
|
80
|
+
#setters = klass.accessible_attributes.sort.collect( &:to_s )
|
81
|
+
|
82
|
+
# remove methodsa that start with '_'
|
83
|
+
@keep_only_pure_setters ||= Regexp.new(/^[a-zA-Z]\w+=/)
|
84
|
+
|
85
|
+
setters = klass.instance_methods.grep(@keep_only_pure_setters).sort.collect( &:to_s )
|
86
|
+
setters.uniq
|
87
|
+
end
|
89
88
|
|
90
89
|
def self.add( klass, operator, type = :assignment)
|
91
90
|
method_details_mgr = get_method_details_mgr( klass )
|
@@ -119,14 +118,14 @@ module DataShift
|
|
119
118
|
method_details_mgrs[klass] = method_details_mgr
|
120
119
|
|
121
120
|
end
|
122
|
-
|
121
|
+
|
123
122
|
# TODO - check out regexp to do this work better plus Inflections ??
|
124
123
|
# Want to be able to handle any of ["Count On hand", 'count_on_hand', "Count OnHand", "COUNT ONHand" etc]
|
125
124
|
def self.substitutions(external_name)
|
126
125
|
name = external_name.to_s
|
127
126
|
|
128
127
|
[
|
129
|
-
name,
|
128
|
+
name.downcase,
|
130
129
|
name.tableize,
|
131
130
|
name.gsub(' ', '_'),
|
132
131
|
name.gsub(' ', '_').downcase,
|
@@ -137,18 +136,24 @@ module DataShift
|
|
137
136
|
]
|
138
137
|
end
|
139
138
|
|
139
|
+
|
140
140
|
# Find the proper format of name, appropriate call + column type for a given name.
|
141
141
|
# e.g Given users entry in spread sheet check for pluralization, missing underscores etc
|
142
142
|
#
|
143
143
|
# If not nil, returned method can be used directly in for example klass.new.send( call, .... )
|
144
144
|
#
|
145
|
-
def self.find_method_detail( klass, external_name )
|
145
|
+
def self.find_method_detail( klass, external_name, conditions = nil )
|
146
146
|
|
147
147
|
method_details_mgr = get_method_details_mgr( klass )
|
148
|
-
|
149
|
-
#
|
150
|
-
|
151
|
-
|
148
|
+
|
149
|
+
# first try for an exact match across all association types
|
150
|
+
MethodDetail::supported_types_enum.each do |t|
|
151
|
+
method_detail = method_details_mgr.find(external_name, t)
|
152
|
+
return method_detail.clone if(method_detail)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Now try various alternatives of the name
|
156
|
+
substitutions(external_name).each do |n|
|
152
157
|
# Try each association type, returning first that contains matching operator with name n
|
153
158
|
MethodDetail::supported_types_enum.each do |t|
|
154
159
|
method_detail = method_details_mgr.find(n, t)
|
@@ -165,14 +170,25 @@ module DataShift
|
|
165
170
|
|
166
171
|
method_details_mgr = get_method_details_mgr( klass )
|
167
172
|
|
168
|
-
|
169
|
-
|
170
|
-
|
173
|
+
# first try for an exact match across all association types
|
174
|
+
MethodDetail::supported_types_enum.each do |t|
|
175
|
+
method_detail = method_details_mgr.find(external_name, t)
|
176
|
+
return method_detail.clone if(method_detail && method_detail.col_type)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Now try various alternatives
|
180
|
+
substitutions(external_name).each do |n|
|
181
|
+
# Try each association type, returning first that contains matching operator with name n
|
182
|
+
MethodDetail::supported_types_enum.each do |t|
|
183
|
+
method_detail = method_details_mgr.find(n, t)
|
184
|
+
return method_detail.clone if(method_detail && method_detail.col_type)
|
185
|
+
end
|
171
186
|
end
|
172
|
-
|
187
|
+
|
173
188
|
nil
|
174
189
|
end
|
175
190
|
|
191
|
+
|
176
192
|
def self.clear
|
177
193
|
belongs_to.clear
|
178
194
|
has_many.clear
|
@@ -190,7 +206,8 @@ module DataShift
|
|
190
206
|
method_details_mgrs[klass] || MethodDetailsManager.new( klass )
|
191
207
|
end
|
192
208
|
|
193
|
-
|
209
|
+
|
210
|
+
# Store a Mgr per mapped klass
|
194
211
|
def self.method_details_mgrs
|
195
212
|
@method_details_mgrs ||= {}
|
196
213
|
@method_details_mgrs
|