datashift 0.10.1 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/datashift.gemspec +13 -6
- data/lib/datashift.rb +2 -20
- data/lib/datashift/exceptions.rb +2 -0
- data/lib/datashift/method_detail.rb +15 -29
- data/lib/datashift/method_dictionary.rb +36 -21
- data/lib/datashift/method_mapper.rb +56 -16
- data/lib/datashift/populator.rb +23 -0
- data/lib/datashift/querying.rb +86 -0
- data/lib/generators/csv_generator.rb +1 -4
- data/lib/generators/excel_generator.rb +28 -11
- data/lib/generators/generator_base.rb +12 -0
- data/lib/loaders/csv_loader.rb +9 -3
- data/lib/loaders/excel_loader.rb +14 -6
- data/lib/loaders/loader_base.rb +38 -125
- data/lib/loaders/paperclip/attachment_loader.rb +130 -62
- data/lib/loaders/paperclip/datashift_paperclip.rb +46 -12
- data/lib/loaders/paperclip/image_loading.rb +25 -41
- data/lib/thor/generate.thor +16 -6
- data/lib/thor/paperclip.thor +25 -5
- data/spec/Gemfile +3 -2
- data/spec/MissingAttachmentRecords/DEMO_001_ror_bag.jpeg +0 -0
- data/spec/{fixtures/images/DEMO_002_Powerstation.jpg → MissingAttachmentRecords/DEMO_002_Powerstation.jpeg} +0 -0
- data/spec/MissingAttachmentRecords/DEMO_002_Powerstation.jpg +0 -0
- data/spec/MissingAttachmentRecords/DEMO_003_ror_mug.jpeg +0 -0
- data/spec/MissingAttachmentRecords/DEMO_004_ror_ringer.jpeg +0 -0
- data/spec/excel_generator_spec.rb +28 -0
- data/spec/excel_loader_spec.rb +12 -17
- data/spec/fixtures/config/database.yml +1 -1
- data/spec/fixtures/db/datashift_test_models_db.sqlite +0 -0
- data/spec/fixtures/db/migrate/20121009161700_add_digitals.rb +24 -0
- data/spec/fixtures/images/DEMO_002_Powerstation.jpeg +0 -0
- data/spec/fixtures/models/digital.rb +14 -0
- data/spec/fixtures/models/owner.rb +5 -3
- data/spec/fixtures/test_model_defs.rb +4 -62
- data/spec/loader_spec.rb +42 -50
- data/spec/method_dictionary_spec.rb +3 -10
- data/spec/method_mapper_spec.rb +79 -20
- data/spec/paperclip_loader_spec.rb +95 -0
- data/spec/spec_helper.rb +44 -8
- metadata +236 -224
- data/lib/helpers/rake_utils.rb +0 -42
- data/spec/fixtures/models/test_model_defs.rb +0 -67
@@ -10,95 +10,163 @@ require 'datashift_paperclip'
|
|
10
10
|
|
11
11
|
module DataShift
|
12
12
|
|
13
|
-
|
13
|
+
module Paperclip
|
14
|
+
|
15
|
+
class AttachmentLoader < LoaderBase
|
14
16
|
|
15
|
-
|
17
|
+
include DataShift::Paperclip
|
16
18
|
|
17
|
-
|
19
|
+
attr_accessor :attach_to_klass, :attach_to_find_by_field
|
18
20
|
|
19
|
-
|
21
|
+
attr_writer :attach_to_field
|
22
|
+
attr_reader :attachment_path, :loading_files_cache
|
23
|
+
|
24
|
+
|
25
|
+
# If we have instantiated a method detail based on the attch to class and fields
|
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
|
+
|
32
|
+
# Options
|
33
|
+
|
34
|
+
# => :attach_to_klass
|
35
|
+
# A class that has a relationship with - has_many, has_one or belongs_to - the attachment
|
36
|
+
# The instance of :attach_to_klass can be searched for and the new attachment assigned.
|
37
|
+
# Examples
|
38
|
+
# Owner has_many pdfs and mp3 files as Digitals .... :attach_to_klass = Owner
|
39
|
+
# User has a single image used as an avatar ... :attach_to_klass = User
|
40
|
+
#
|
41
|
+
# => :attach_to_find_by_field
|
42
|
+
# For the :attach_to_klass, this is the field used to search for the parent
|
43
|
+
# object to assign the new attachment to.
|
44
|
+
# Examples
|
45
|
+
# Owner has a unique 'name' field ... :attach_to_find_by_field = :name
|
46
|
+
# User has a unique 'login' field ... :attach_to_klass = :login
|
47
|
+
#
|
48
|
+
# => :attach_to_field
|
49
|
+
# Attribute/association to assign attachment to on :attach_to_klass.
|
50
|
+
# Examples
|
51
|
+
# :attach_to_field => digitals : Owner.digitals = attachment
|
52
|
+
# :attach_to_field => avatar : User.avatar = attachment
|
53
|
+
#
|
54
|
+
#
|
55
|
+
def initialize(attachment_klazz, attachment = nil, options = {})
|
20
56
|
|
21
|
-
|
57
|
+
init_from_options( options )
|
58
|
+
|
59
|
+
opts = options.merge(:load => false)
|
22
60
|
|
23
|
-
|
61
|
+
super( attachment_klazz, attachment, opts )
|
62
|
+
|
63
|
+
puts "Attachment Class is #{load_object_class}" if(@verbose)
|
64
|
+
|
65
|
+
raise "Failed to create Attachment for loading" unless @load_object
|
66
|
+
end
|
67
|
+
|
68
|
+
def init_from_options( options )
|
69
|
+
|
70
|
+
@attach_to_klass = options[:attach_to_klass] || @attach_to_klass || nil
|
24
71
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
72
|
+
unless(@attach_to_klass.nil? || (MethodDictionary::for?(@attach_to_klass) && options[:reload] == false))
|
73
|
+
#puts "Building Method Dictionary for class #{object_class}"
|
74
|
+
DataShift::MethodDictionary.find_operators( @attach_to_klass, :reload => options[:reload], :instance_methods => true )
|
75
|
+
|
76
|
+
# Create dictionary of data on all possible 'setter' methods which can be used to
|
77
|
+
# populate or integrate an object of type @load_object_class
|
78
|
+
DataShift::MethodDictionary.build_method_details(@attach_to_klass)
|
79
|
+
end
|
31
80
|
|
32
|
-
|
81
|
+
@attach_to_find_by_field = options[:attach_to_find_by_field] || @attach_to_find_by_field || nil
|
82
|
+
@attach_to_field = options[:attach_to_field] || @attach_to_field || nil
|
83
|
+
|
84
|
+
unless(@attach_to_klass.nil? && @attach_to_field.nil? )
|
85
|
+
@attach_to_method_detail = MethodDictionary.find_method_detail(@attach_to_klass, @attach_to_field)
|
86
|
+
end
|
87
|
+
end
|
33
88
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
89
|
+
# This version creates attachments and also attaches them to instances of :attach_to_klazz
|
90
|
+
#
|
91
|
+
# Options
|
92
|
+
# :split_file_name_on Used in scan process to progresivly split filename to find
|
93
|
+
# :attach_to_klass with matching :attach_to_find_by_field
|
94
|
+
#
|
95
|
+
#
|
96
|
+
def process_from_filesystem(path, options = {} )
|
97
|
+
|
98
|
+
init_from_options( options )
|
39
99
|
|
40
|
-
|
100
|
+
raise "The class that attachments belongs to has not been set (:attach_to_klass)" unless(@attach_to_klass)
|
101
|
+
|
102
|
+
raise "The field to search for attachment's owner has not been set (:attach_to_find_by_field)" unless(@attach_to_find_by_field)
|
103
|
+
|
104
|
+
@load_object = options[:attachment] if(options[:attachment])
|
105
|
+
|
106
|
+
@attachment_path = path
|
41
107
|
|
42
|
-
|
108
|
+
missing_records = []
|
43
109
|
|
44
|
-
|
45
|
-
|
110
|
+
# try splitting up filename in various ways looking for the attachment owqner
|
111
|
+
split_on = @config['split_file_name_on'] || options[:split_file_name_on]
|
46
112
|
|
47
|
-
|
113
|
+
@loading_files_cache = Paperclip::get_files(path, options)
|
48
114
|
|
49
|
-
|
50
|
-
|
51
|
-
lookup_field = options[:attach_to_lookup_field]
|
52
|
-
|
53
|
-
cache.each do |file_name|
|
115
|
+
puts "Found #{loading_files_cache.size} files - splitting names on delimiter [#{split_on}]"
|
54
116
|
|
55
|
-
|
117
|
+
loading_files_cache.each do |file_name|
|
56
118
|
|
57
|
-
|
119
|
+
attachment_name = File.basename(file_name)
|
120
|
+
|
121
|
+
logger.info "Processing attachment file #{attachment_name} "
|
58
122
|
|
59
|
-
|
60
|
-
|
123
|
+
base_name = File.basename(file_name, '.*')
|
124
|
+
base_name.strip!
|
125
|
+
|
126
|
+
record = nil
|
61
127
|
|
62
|
-
|
63
|
-
|
64
|
-
record = get_record_by(@attach_to_klass, lookup_field, base_name, split_search_term)
|
128
|
+
puts "try to find record where #{attach_to_find_by_field} == #{base_name}"
|
129
|
+
record = get_record_by(attach_to_klass, attach_to_find_by_field, base_name, split_on)
|
65
130
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
131
|
+
if(record)
|
132
|
+
puts "Found record for attachment :\n#{record.inspect}"
|
133
|
+
logger.info "Found record for attachment : #{record.inspect}"
|
134
|
+
else
|
135
|
+
missing_records << file_name
|
136
|
+
end
|
71
137
|
|
72
|
-
|
138
|
+
next if(options[:dummy]) # Don't actually create/upload to DB if we are doing dummy run
|
73
139
|
|
74
|
-
|
75
|
-
|
76
|
-
|
140
|
+
# Check if attachment must have an associated record
|
141
|
+
if(record)
|
142
|
+
puts "now create attachment"
|
143
|
+
reset()
|
77
144
|
|
78
|
-
|
145
|
+
create_attachment(@load_object_class, file_name, record, attach_to_field, options)
|
79
146
|
|
80
|
-
|
81
|
-
|
147
|
+
puts "Added Attachment #{File.basename(file_name)} to #{record.send(attach_to_find_by_field)} : #{record.id}" if(@verbose)
|
148
|
+
end
|
82
149
|
|
83
|
-
|
150
|
+
end
|
84
151
|
|
85
|
-
|
86
|
-
|
152
|
+
unless missing_records.empty?
|
153
|
+
FileUtils.mkdir_p('MissingAttachmentRecords') unless File.directory?('MissingAttachmentRecords')
|
87
154
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
155
|
+
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 a copy of files with MISSING #{@load_object_class} : ./MissingAttachmentRecords"
|
157
|
+
missing_records.each do |i|
|
158
|
+
puts "Copying #{i} to MissingAttachmentRecords folder" if(options[:verbose])
|
159
|
+
FileUtils.cp( i, 'MissingAttachmentRecords') unless(options[:dummy] == 'true')
|
160
|
+
end
|
161
|
+
else
|
162
|
+
puts "Created #{loading_files_cache.size} #{@load_object_class} attachments and succesfully attached to a #{@attach_to_klass}"
|
93
163
|
end
|
94
|
-
else
|
95
|
-
puts "All files (#{cache.size}) were succesfully attached to a #{@load_object_class}"
|
96
|
-
end
|
97
164
|
|
98
|
-
|
165
|
+
puts "Dummy Run Complete- if happy run without -d" if(options[:dummy])
|
99
166
|
|
100
|
-
|
167
|
+
end
|
101
168
|
|
102
|
-
|
169
|
+
end
|
103
170
|
|
104
|
-
end
|
171
|
+
end
|
172
|
+
end
|
@@ -6,12 +6,17 @@
|
|
6
6
|
# Details:: Module containing common functionality for working with Paperclip attachments
|
7
7
|
#
|
8
8
|
require 'logging'
|
9
|
+
require 'paperclip'
|
9
10
|
|
10
11
|
module DataShift
|
11
12
|
|
12
13
|
module Paperclip
|
13
14
|
|
14
15
|
include DataShift::Logging
|
16
|
+
include DataShift::Logging
|
17
|
+
require 'paperclip/attachment_loader'
|
18
|
+
|
19
|
+
attr_accessor :attachment
|
15
20
|
|
16
21
|
# Get all image files (based on file extensions) from supplied path.
|
17
22
|
# Options :
|
@@ -44,28 +49,57 @@ module DataShift
|
|
44
49
|
|
45
50
|
# Note the paperclip attachment model defines the storage path via something like :
|
46
51
|
# => :path => ":rails_root/public/blah/blahs/:id/:style/:basename.:extension"
|
52
|
+
#
|
47
53
|
# Options
|
48
|
-
#
|
54
|
+
#
|
55
|
+
# :attributes
|
56
|
+
#
|
57
|
+
# Pass through hash of attributes to klass initializer
|
58
|
+
#
|
59
|
+
# :has_attached_file_attribute
|
60
|
+
#
|
61
|
+
# Paperclip attachment name defined with macro 'has_attached_file :name'
|
62
|
+
#
|
49
63
|
# e.g
|
50
|
-
#
|
51
|
-
#
|
64
|
+
# When : has_attached_file :avatar
|
65
|
+
#
|
66
|
+
# Give : {:has_attached_file_attribute => :avatar}
|
67
|
+
#
|
68
|
+
# When : has_attached_file :icon
|
52
69
|
#
|
53
|
-
#
|
54
|
-
|
70
|
+
# Give : { :has_attached_file_attribute => :icon }
|
71
|
+
#
|
55
72
|
def create_attachment(klass, attachment_path, record = nil, attach_to_record_field = nil, options = {})
|
56
73
|
|
57
|
-
|
58
|
-
|
59
|
-
|
74
|
+
has_attached_file_attribute = options[has_attached_file_attribute] ? options[:has_attached_file_attribute].to_sym : :attachment
|
75
|
+
|
76
|
+
attributes = { has_attached_file_attribute => get_file(attachment_path) }
|
60
77
|
|
78
|
+
attributes.merge!(options[:attributes]) if(options[:attributes])
|
79
|
+
|
80
|
+
# e.g (:viewable => some_product) = Icon
|
81
|
+
|
82
|
+
#attributes.merge!(attach_to_record_field.to_sym => record) if(record && attach_to_record_field)
|
83
|
+
|
61
84
|
begin
|
62
85
|
|
63
|
-
attachment =
|
64
|
-
|
86
|
+
@attachment = klass.new(attributes, :without_protection => true)
|
87
|
+
|
88
|
+
if(@attachment.save)
|
89
|
+
puts "Success: Created Attachment #{@attachment.id} : #{@attachment.attachment_file_name}"
|
90
|
+
|
91
|
+
if(attach_to_record_field.is_a? MethodDetail)
|
92
|
+
attach_to_record_field.assign(record, @attachment)
|
93
|
+
else
|
94
|
+
# assume its not a has_many and try basic send
|
95
|
+
record.send(attach_to_record_field + '=', @attachment)
|
96
|
+
end if(record && attach_to_record_field)
|
97
|
+
|
65
98
|
else
|
66
|
-
|
99
|
+
puts "ERROR : Problem saving to DB : #{@attachment.inspect}"
|
67
100
|
end
|
68
|
-
|
101
|
+
|
102
|
+
@attachment
|
69
103
|
rescue => e
|
70
104
|
puts "PaperClip error - Problem creating Attachment from : #{attachment_path}"
|
71
105
|
puts e.inspect, e.backtrace
|
@@ -6,59 +6,43 @@
|
|
6
6
|
# => Provides facilities for bulk uploading/exporting attachments provided by PaperClip
|
7
7
|
# gem
|
8
8
|
require 'datashift_paperclip'
|
9
|
+
require 'attachment_loader'
|
9
10
|
|
10
11
|
module DataShift
|
11
12
|
|
12
13
|
module ImageLoading
|
13
14
|
|
14
|
-
include DataShift::Paperclip
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# :glob : The glob to use to find files
|
19
|
-
# => :recursive : Descend tree looking for files rather than just supplied path
|
20
|
-
|
21
|
-
def self.get_files(path, options = {})
|
22
|
-
glob = options[:glob] ? options[:glob] : image_magik_glob
|
23
|
-
glob = (options['recursive'] || options[:recursive]) ? "**/#{glob}" : glob
|
24
|
-
|
25
|
-
Dir.glob("#{path}/#{glob}", File::FNM_CASEFOLD)
|
15
|
+
include DataShift::Paperclip
|
16
|
+
|
17
|
+
def initialize(attachment_klazz, attachment = nil, options = {})
|
18
|
+
super( attachment_klazz, attachment, options )
|
26
19
|
end
|
27
|
-
|
28
20
|
|
29
21
|
# Note the paperclip attachment model defines the storage path via something like :
|
22
|
+
#
|
30
23
|
# => :path => ":rails_root/public/blah/blahs/:id/:style/:basename.:extension"
|
24
|
+
#
|
31
25
|
# Options
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
#
|
27
|
+
# See also DataShift::paperclip create_attachment for more options
|
28
|
+
#
|
29
|
+
# Example: Image is a model class with an attachment.
|
30
|
+
# Image table contains a viewable field which can contain other models,
|
31
|
+
# such as Product, User etc all of which can have an Image
|
32
|
+
#
|
33
|
+
# :viewable_record
|
34
|
+
#
|
35
|
+
def create_attachment(klass, attachment_path, record = nil, attach_to_record_field = nil, options = {})
|
36
|
+
|
37
|
+
attachment_options = options.dup
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
(viewable_record and viewable_record.respond_to? :name) ? viewable_record.name : ""
|
42
|
-
end
|
43
|
-
|
44
|
-
position = (viewable_record and viewable_record.respond_to?(:images)) ? viewable_record.images.length : 0
|
45
|
-
|
46
|
-
file = get_file(attachment_path)
|
39
|
+
attributes = {:alt => (options[:alt] || "") }
|
40
|
+
|
41
|
+
attributes[:position] = (!options[:position] && record and record.respond_to?(:images)) ? record.images.length : 0
|
47
42
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
{has_attached_file.to_sym => file, :viewable => viewable_record, :alt => alt, :position => position},
|
52
|
-
:without_protection => true
|
53
|
-
)
|
54
|
-
|
55
|
-
#image.attachment.reprocess! not sure this is required anymore
|
56
|
-
|
57
|
-
puts image.save ? "Success: Created Image: #{image.id} : #{image.attachment_file_name}" : "ERROR : Problem saving to DB Image: #{image.inspect}"
|
58
|
-
rescue => e
|
59
|
-
puts "PaperClip error - Problem creating an Image from : #{attachment_path}"
|
60
|
-
puts e.inspect, e.backtrace
|
61
|
-
end
|
43
|
+
attachment_options.merge!( attributes )
|
44
|
+
|
45
|
+
super(klass, attachment_path, record, attach_to_record_field, attachment_options)
|
62
46
|
end
|
63
47
|
|
64
48
|
# Set of file extensions ImageMagik can process so default glob
|
data/lib/thor/generate.thor
CHANGED
@@ -17,20 +17,24 @@
|
|
17
17
|
# bundle exec thor help datashift:generate:excel
|
18
18
|
#
|
19
19
|
require 'datashift'
|
20
|
-
|
20
|
+
require 'excel_generator'
|
21
|
+
|
21
22
|
# Note, not DataShift, case sensitive, create namespace for command line : datashift
|
22
23
|
module Datashift
|
23
24
|
|
24
25
|
|
25
26
|
class Generate < Thor
|
26
|
-
|
27
|
+
|
27
28
|
include DataShift::Logging
|
28
29
|
|
29
30
|
desc "excel", "generate a template from an active record model (with optional associations)"
|
30
31
|
method_option :model, :aliases => '-m', :required => true, :desc => "The active record model to export"
|
31
32
|
method_option :result, :aliases => '-r', :required => true, :desc => "Create template of model in supplied file"
|
32
33
|
method_option :assoc, :aliases => '-a', :type => :boolean, :desc => "Include all associations in the template"
|
33
|
-
method_option :exclude, :aliases => '-
|
34
|
+
method_option :exclude, :aliases => '-x', :type => :array, :desc => "Use with -a : Exclude association types. Any from #{DataShift::MethodDetail::supported_types_enum.to_a.inspect}"
|
35
|
+
method_option :remove, :aliases => '-e', :type => :array, :desc => "Don't generate in template the supplied fields"
|
36
|
+
method_option :remove_rails, :type => :boolean, :desc => "Don't generate in template the standard Rails fields: #{DataShift::GeneratorBase::rails_columns.inspect}"
|
37
|
+
|
34
38
|
|
35
39
|
def excel()
|
36
40
|
|
@@ -38,7 +42,7 @@ module Datashift
|
|
38
42
|
# ...can we make this more robust ? e.g what about when using active record but not in Rails app,
|
39
43
|
require File.expand_path('config/environment.rb')
|
40
44
|
|
41
|
-
|
45
|
+
|
42
46
|
|
43
47
|
model = options[:model]
|
44
48
|
result = options[:result]
|
@@ -58,12 +62,18 @@ module Datashift
|
|
58
62
|
begin
|
59
63
|
gen = DataShift::ExcelGenerator.new(result)
|
60
64
|
|
65
|
+
opts = { :remove => options[:remove],
|
66
|
+
:remove_rails => options[:remove_rails]
|
67
|
+
}
|
68
|
+
|
61
69
|
if(options[:assoc])
|
62
|
-
|
70
|
+
|
71
|
+
opts[:exclude] = options[:exclude]
|
72
|
+
|
63
73
|
logger.info("Datashift: Generating with associations")
|
64
74
|
gen.generate_with_associations(klass, opts)
|
65
75
|
else
|
66
|
-
gen.generate(klass)
|
76
|
+
gen.generate(klass, opts)
|
67
77
|
end
|
68
78
|
rescue => e
|
69
79
|
puts e
|