rseed 0.0.1 → 1.0.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.
- checksums.yaml +8 -8
- data/README.rdoc +42 -40
- data/lib/generators/rseed/converter.rb +14 -2
- data/lib/generators/rseed/templates/converter.rb.erb +25 -5
- data/lib/rseed/attribute.rb +4 -3
- data/lib/rseed/converter.rb +1 -0
- data/lib/rseed/{utilities.rb → helpers.rb} +18 -4
- data/lib/rseed/processor.rb +43 -27
- data/lib/rseed/version.rb +1 -1
- data/lib/rseed.rb +1 -1
- data/lib/tasks/rseed.rake +11 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YTljZGYyMzkyZjE0MzQyZjVhYTdiZGM5YmMwNjdiM2RmN2M3ODFkNw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NWM2ZDNmNmRhMmU0NjdkNzk5YjQ2ODAxMmZiYzM2YzYzYTU4MWI0ZQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MjMxM2IyY2VkZTI4YTg4MGQzNzk2MzMzMmYxYTdlYmQ4YzI5YTgyYmQ0NjQz
|
10
|
+
ZmYyMWJhYzkwN2QzYWUzOGRmZTdiNDIwMDQ1NTljOGQ2MjJjZjY0ZjRlYjAw
|
11
|
+
MDAyMDcyZmQ2M2IyZjA1ZjY4Y2QyYjQ1N2E5MjM2ZTRmOWEyNjU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZmU5YWQ5ZmY1N2MwOWRlZWE0ZDI0OTNjMmEwYjAzMjE2NWI5ZjU4ZjZhOWQ4
|
14
|
+
YThkZThmYWFlNDAxNzNmZWQzMDk3Njg3NjgxZDI5YTQ3MWI0MDkyNzk1MzJk
|
15
|
+
YzE3ZGQ5MWEwNDkwNDIxY2MyYzYxNjkxODdiZTY0OTIxYjYyZGE=
|
data/README.rdoc
CHANGED
@@ -1,7 +1,17 @@
|
|
1
1
|
= Rseed
|
2
2
|
|
3
|
-
Rseed is a
|
4
|
-
|
3
|
+
Rseed is a featureful library and bunch of utilities to assist in importing mass data (or even a few lines of data) into
|
4
|
+
your Rails project.
|
5
|
+
|
6
|
+
Rseed is a replacement for active_import (http://github.com/intrica/active_import). There are lots of improvements in
|
7
|
+
order to make it easy to create and maintain converters.
|
8
|
+
|
9
|
+
Rseed can also import from excel files with the optional rseed-roo gem (http://github.com/intrica/rseed-roo).
|
10
|
+
|
11
|
+
== Requirements
|
12
|
+
|
13
|
+
>= Rails 3.0
|
14
|
+
>= Ruby 1.9
|
5
15
|
|
6
16
|
== Installation
|
7
17
|
|
@@ -15,36 +25,47 @@ Then run:
|
|
15
25
|
|
16
26
|
== Quick Example
|
17
27
|
|
18
|
-
rails g rseed:converter
|
28
|
+
rails g rseed:converter HtmlColor
|
19
29
|
|
20
30
|
This will create an model converter in the directory app/rseed. You can read through this import file to see how the import works.
|
21
31
|
|
22
|
-
This also creates a default data file in db/rseed. This will be the CSV used for this converter.
|
32
|
+
This also creates a default data file of html_colors.csv in db/rseed. This will be the CSV used for this converter.
|
33
|
+
|
34
|
+
== Generators
|
35
|
+
|
36
|
+
The generator will automatically create attribute lines for all of the attributes in the model except :id, :created_at, :updated_at.
|
37
|
+
|
38
|
+
Generator options are as follows:
|
39
|
+
|
40
|
+
* --attribute
|
41
|
+
This will set up the converter to do a *first_or_initilize* on the specified attribute instead of a *new*
|
42
|
+
|
43
|
+
* --minimal
|
44
|
+
This will cause the generator to create a file with fewer
|
23
45
|
|
24
46
|
== The Converter File
|
25
47
|
|
26
|
-
=== Options
|
48
|
+
=== Attribute Options
|
27
49
|
|
28
|
-
:header
|
50
|
+
* :header
|
29
51
|
Defines the name of the attribute to be used for serialization. If there is no :match defined, it will also be used
|
30
52
|
to match the attribute name of the input to the attribute being defined.
|
31
53
|
|
32
|
-
:match
|
54
|
+
* :match
|
33
55
|
A regex string that is used to match the attribute name of the input to the attribute being defined. If this is not
|
34
56
|
defined, a match will be checked against :header and then the attribute name.
|
35
57
|
|
36
|
-
:type
|
58
|
+
* :type
|
37
59
|
Defines a type for the string.
|
38
60
|
|
39
|
-
:model
|
61
|
+
* :model
|
40
62
|
This can be set to the name of a model that this attribute should resolve to. The model is classified so using a symbol
|
41
|
-
works here. Alternately, if
|
42
|
-
order for this to work, :model_attribute must also be set.
|
63
|
+
works here. Alternately, if only the :model_attribute is set, the name of the attribute will be used as the model name.
|
43
64
|
|
44
|
-
:model_attribute
|
65
|
+
* :model_attribute
|
45
66
|
Specify which attribute on the model is used for lookup.
|
46
67
|
|
47
|
-
:model_match
|
68
|
+
* :model_match
|
48
69
|
Specifies how the model should be resolved. The value here is called against the *where* that is used to look up the model.
|
49
70
|
For example, this defaults to *:first*. If your model is *Person* and the :model_attribute is *:name* then this is what
|
50
71
|
is called to set the attribute value:
|
@@ -53,7 +74,7 @@ is called to set the attribute value:
|
|
53
74
|
|
54
75
|
You may use any active record method in this case, such as :first_or_create, or :last.
|
55
76
|
|
56
|
-
:optional
|
77
|
+
* :optional
|
57
78
|
Defines the attribute as optionsal. This has no effect in the *HashAdapter*.
|
58
79
|
|
59
80
|
== Rake Tasks
|
@@ -61,40 +82,21 @@ Defines the attribute as optionsal. This has no effect in the *HashAdapter*.
|
|
61
82
|
These rake tasks allow you to run seeds manually:
|
62
83
|
|
63
84
|
rake rseed:csv Load csv file into a model using a model converter
|
64
|
-
rake rseed:seed Seed a list of import files
|
65
85
|
|
66
86
|
=== Examples
|
67
87
|
|
68
|
-
rake rseed:csv
|
88
|
+
rake rseed:csv file=users.csv converter=User converter_options="give_admin_access=true"
|
69
89
|
|
70
|
-
In this case the file in db/rseed/
|
71
|
-
|
72
|
-
The FILE parameter is not strictly necessary in this case either as the default file name will be an underscored value of the name of the converter.
|
90
|
+
In this case the file in db/rseed/users.csv would be run through the converter UserConverter. The options specified are available within the converter. In this case options["give_admin_access"] will evaluate to "true".
|
73
91
|
|
74
92
|
== Seeding
|
75
93
|
|
76
|
-
|
77
|
-
|
78
|
-
=== Example
|
79
|
-
|
80
|
-
user_info/user.xls | User | give_admin_access=false,send_email=true
|
81
|
-
user_info/roles.xls | Role
|
82
|
-
user_info/permissions.xls | UserPermission
|
83
|
-
|
84
|
-
You could save this file as db/rseed/user_info.seed and run the command like this:
|
85
|
-
|
86
|
-
rake rseed:seed SET=user_info
|
87
|
-
|
88
|
-
Note this will get the data files from a subdirectory: db/rseed/user_info. Also the top conversion uses options but as they are optional, the following two do not.
|
89
|
-
|
90
|
-
If you do not specify a set, the rake task will look for a set based on the current development environment: ie development.seed.
|
91
|
-
|
94
|
+
If you want to seed your Rails application using Rseed. The best method is to add lines like the following to db/seeds.rb
|
92
95
|
|
93
|
-
|
96
|
+
Rseed::from_csv 'html_colors.csv', converter: :html_color, converter_options: { no_red: true }
|
94
97
|
|
95
|
-
|
98
|
+
You can always use *if* statements in this file to filter seeds by Rails.env or something similar.
|
96
99
|
|
97
|
-
|
98
|
-
==== Column Types
|
100
|
+
=== Custom Type Conversions
|
99
101
|
|
100
|
-
|
102
|
+
TODO
|
@@ -4,6 +4,8 @@ module Rseed
|
|
4
4
|
module Generators
|
5
5
|
class ConverterGenerator < Rails::Generators::NamedBase
|
6
6
|
class_option :converter_name, :type => :string, :default => nil, :desc => "Names the converter file, defaults to the model name"
|
7
|
+
class_option :attribute, :type => :string, :default => nil, :desc => "If set will default to a first_or_initialize for creation of the model."
|
8
|
+
class_option :minimal, :type => :boolean, :default => false, :desc => "Create a converter with less template code and less comments"
|
7
9
|
|
8
10
|
def self.source_root
|
9
11
|
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
@@ -15,12 +17,22 @@ module Rseed
|
|
15
17
|
Dir.mkdir(converter_dir) unless File.directory?(converter_dir)
|
16
18
|
@model_name = file_name
|
17
19
|
@class_name = class_name
|
18
|
-
@model =
|
20
|
+
@model = eval(@class_name)
|
19
21
|
@columns = @model.columns_hash.except "id", "created_at", "updated_at"
|
22
|
+
@relationships = {}
|
23
|
+
@model.reflect_on_all_associations.each do |relationship|
|
24
|
+
if relationship.macro == :belongs_to
|
25
|
+
@relationships[relationship.name] = relationship
|
26
|
+
@columns.delete(relationship.foreign_key)
|
27
|
+
end
|
28
|
+
end
|
20
29
|
@converter_name = options.converter_name || @class_name
|
30
|
+
@match_attribute = options.attribute
|
31
|
+
@minimal = options.minimal
|
32
|
+
|
21
33
|
template 'converter.rb.erb', File.join(converter_dir, "#{@converter_name.underscore}_converter.rb")
|
22
34
|
Dir.mkdir(seed_dir) unless File.directory?(seed_dir)
|
23
|
-
template 'data.csv.erb', File.join(seed_dir, "#{@converter_name.underscore}.csv")
|
35
|
+
template 'data.csv.erb', File.join(seed_dir, "#{@converter_name.underscore.pluralize}.csv")
|
24
36
|
end
|
25
37
|
end
|
26
38
|
end
|
@@ -4,6 +4,9 @@ class <%= @converter_name.camelize %>Converter < Rseed::Converter
|
|
4
4
|
<% @columns.each_pair do |attribute, meta| -%>
|
5
5
|
attribute :<%= attribute %>, type: :<%= meta.type %>
|
6
6
|
<% end -%>
|
7
|
+
<% @relationships.each_pair do |attribute, meta| -%>
|
8
|
+
attribute :<%= attribute %><% unless @minimal %>, model: <%= meta.class_name %><% end %>, model_attribute: :<%= meta.foreign_key %><% unless @minimal %>, model_match: :first<% end %>
|
9
|
+
<% end -%>
|
7
10
|
|
8
11
|
def <%= @model_name %>_attributes values
|
9
12
|
attributes = {}
|
@@ -14,26 +17,43 @@ class <%= @converter_name.camelize %>Converter < Rseed::Converter
|
|
14
17
|
|
15
18
|
return attributes
|
16
19
|
end
|
20
|
+
<% unless @minimal -%>
|
17
21
|
|
18
22
|
def before_deserialize
|
23
|
+
true
|
19
24
|
end
|
20
25
|
|
21
26
|
def after_deserialize
|
22
27
|
end
|
28
|
+
<% end -%>
|
23
29
|
|
24
30
|
def deserialize values
|
25
31
|
# Prevents nil values coming from the import overwriting the model attributes
|
26
32
|
remove_nil_from values
|
33
|
+
<% unless @minimal -%>
|
34
|
+
|
27
35
|
# Prevents blank values coming from the import overwriting the model attributes
|
28
36
|
# remove_blank_from values
|
29
37
|
|
30
38
|
# For create only
|
31
|
-
<%= @model_name %> = <%= @class_name %>.new
|
39
|
+
# <%= @model_name %> = <%= @class_name %>.new
|
40
|
+
#
|
32
41
|
# For create or update, use the following instead and change the match attribute name as required
|
33
|
-
#
|
34
|
-
|
42
|
+
# <%= @model_name %> = <%= @class_name %>.where(:id => values[:id]).first_or_initialize
|
43
|
+
<% end -%>
|
44
|
+
|
45
|
+
<% if @match_attribute -%>
|
46
|
+
<%= @model_name %> = <%= @class_name %>.where(:<%= @match_attribute %> => values[:<%= @match_attribute %>]).first_or_initialize
|
47
|
+
<% else -%>
|
48
|
+
<%= @model_name %> = <%= @class_name %>.new
|
49
|
+
<% end -%>
|
50
|
+
|
51
|
+
success = <%= @model_name %>.update_attributes <%= @model_name %>_attributes(values)
|
52
|
+
|
53
|
+
# This will return false and set errors if the record fails to update, signalling a failed import
|
54
|
+
return fail_with_error <%= @model_name %>.errors unless success
|
35
55
|
|
36
|
-
#
|
37
|
-
|
56
|
+
# Return true signalling a successful import
|
57
|
+
true
|
38
58
|
end
|
39
59
|
end
|
data/lib/rseed/attribute.rb
CHANGED
@@ -25,10 +25,11 @@ module Rseed
|
|
25
25
|
return nil if values[self.name].nil?
|
26
26
|
value = values[self.name]
|
27
27
|
|
28
|
-
if options[:
|
28
|
+
if options[:model_attribute]
|
29
|
+
model = options[:model] || true
|
29
30
|
# The attribute is a model, we look up the model via the specified attribute
|
30
|
-
model_name =
|
31
|
-
klass = model_name.to_s.classify.constantize
|
31
|
+
model_name = model == true ? self.name : model
|
32
|
+
klass = model_name.is_a?(Class) ? model_name : model_name.to_s.classify.constantize
|
32
33
|
model_match = options[:model_match] || :first
|
33
34
|
value = klass.where(options[:model_attribute] => value).send(model_match.to_s)
|
34
35
|
elsif options[:type]
|
data/lib/rseed/converter.rb
CHANGED
@@ -16,7 +16,7 @@ module Rseed
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def process_with_status_bar processor, options = {}
|
19
|
-
title = options[:title] ? options[:title] : "Seed"
|
19
|
+
title = options[:title] ? options[:title].dup : "Seed"
|
20
20
|
title = "#{processor.converter.name.cyan} #{title.blue}"
|
21
21
|
record_count = 0
|
22
22
|
progress_bar = ProgressBar.create(starting_at: nil, total: nil, format: "#{"Preprocessing".magenta} %t <%B>", title: title)
|
@@ -37,19 +37,21 @@ module Rseed
|
|
37
37
|
end
|
38
38
|
when :complete
|
39
39
|
progress_bar.format "#{"Complete".green} %t <%B> %C (%a)"
|
40
|
-
progress_bar.finish
|
40
|
+
progress_bar.finish if progress_bar.total
|
41
41
|
when :error
|
42
42
|
processor.logger.error result[:message].to_s.red
|
43
43
|
processor.logger.error result[:error]
|
44
|
-
processor.logger.error result[:backtrace].join('\n')
|
44
|
+
processor.logger.error result[:backtrace].join('\n') if result[:backtrace]
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
def from_file file, options = {}
|
50
|
+
f = import_file(file)
|
51
|
+
return false unless f
|
50
52
|
p = Processor.new(options)
|
51
53
|
return nil unless p
|
52
|
-
p.adapter.file =
|
54
|
+
p.adapter.file = f
|
53
55
|
process_with_status_bar p, title: file
|
54
56
|
end
|
55
57
|
|
@@ -65,8 +67,20 @@ module Rseed
|
|
65
67
|
process_with_status_bar p, title: "Hash"
|
66
68
|
end
|
67
69
|
|
70
|
+
def import_file(file)
|
71
|
+
return file if File.exists? file
|
72
|
+
# Try it relative to the rseed directory
|
73
|
+
rseed_file = File.join(Rails.root, 'db', 'rseed', file)
|
74
|
+
return rseed_file if File.exists? rseed_file
|
75
|
+
# Try it relative to the Rails directory
|
76
|
+
rails_file = File.join(Rails.root, file)
|
77
|
+
return rails_file if File.exists? rails_file
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
68
81
|
module_function :from_file
|
69
82
|
module_function :from_hash
|
70
83
|
module_function :from_csv
|
71
84
|
module_function :process_with_status_bar
|
85
|
+
module_function :import_file
|
72
86
|
end
|
data/lib/rseed/processor.rb
CHANGED
@@ -10,7 +10,18 @@ module Rseed
|
|
10
10
|
|
11
11
|
adapter = options[:adapter].is_a?(Adapter) ? options[:adapter] : Rseed.const_get("#{options[:adapter].to_s.classify}Adapter").new
|
12
12
|
converter = options[:converter].is_a?(Converter) ? options[:converter] : Rseed.const_get("#{options[:converter].to_s.classify}Converter").new
|
13
|
-
|
13
|
+
if options[:converter_options]
|
14
|
+
converter_options = options[:converter_options]
|
15
|
+
if converter_options.is_a? String
|
16
|
+
options = converter_options.split(";")
|
17
|
+
converter_options = {}
|
18
|
+
options.each do |option|
|
19
|
+
s = option.split("=")
|
20
|
+
converter_options[s[0].strip] = s[1].strip
|
21
|
+
end
|
22
|
+
end
|
23
|
+
converter.options = converter_options
|
24
|
+
end
|
14
25
|
@adapter = adapter
|
15
26
|
@converter = converter
|
16
27
|
end
|
@@ -24,39 +35,44 @@ module Rseed
|
|
24
35
|
adapter.logger = logger
|
25
36
|
adapter.converter = converter
|
26
37
|
|
27
|
-
yield :preprocessing
|
28
38
|
begin
|
39
|
+
logger.info "Converter: #{@converter.name.cyan}"
|
40
|
+
logger.info "Converter Options: #{@converter.options.to_s.dup.cyan}"
|
41
|
+
yield :preprocessing
|
29
42
|
if @adapter.preprocess
|
30
|
-
@converter.before_deserialize
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
if @converter.before_deserialize
|
44
|
+
yield :processing
|
45
|
+
start_time = Time.now
|
46
|
+
adapter.process do |values, meta|
|
47
|
+
result = {values: values}
|
48
|
+
meta ||= {}
|
49
|
+
begin
|
50
|
+
if @converter.deserialize_raw(values)
|
51
|
+
result[:success] = true
|
52
|
+
else
|
53
|
+
result[:success] = false
|
54
|
+
result[:message] = "Failed to convert"
|
55
|
+
result[:error] = @converter.error
|
56
|
+
end
|
57
|
+
rescue Exception => e
|
40
58
|
result[:success] = false
|
41
|
-
result[:message] = "
|
42
|
-
result[:error] =
|
59
|
+
result[:message] = "Exception during deserialize"
|
60
|
+
result[:error] = e.message
|
61
|
+
result[:backtrace] = e.backtrace
|
43
62
|
end
|
44
|
-
rescue Exception => e
|
45
|
-
result[:success] = false
|
46
|
-
result[:message] = "Exception during deserialize"
|
47
|
-
result[:error] = e.message
|
48
|
-
result[:backtrace] = e.backtrace
|
49
|
-
end
|
50
63
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
64
|
+
# Calculate the ETA
|
65
|
+
if meta[:record_count] and meta[:total_records]
|
66
|
+
remaining = meta[:total_records] - meta[:record_count]
|
67
|
+
tpr = (Time.now - start_time)/meta[:record_count]
|
68
|
+
meta[:eta] = remaining * tpr
|
69
|
+
end
|
70
|
+
yield :processing, result, meta
|
56
71
|
end
|
57
|
-
|
72
|
+
@converter.after_deserialize
|
73
|
+
else
|
74
|
+
yield :error, {success: false, message: 'Before deserialize failed', error: @converter.error}
|
58
75
|
end
|
59
|
-
@converter.after_deserialize
|
60
76
|
else
|
61
77
|
yield :error, {success: false, message: 'Preprocessing failed', error: @adapter.error}
|
62
78
|
end
|
data/lib/rseed/version.rb
CHANGED
data/lib/rseed.rb
CHANGED
data/lib/tasks/rseed.rake
CHANGED
@@ -2,7 +2,17 @@ require "rseed"
|
|
2
2
|
require "colorize"
|
3
3
|
|
4
4
|
namespace :rseed do
|
5
|
-
desc "
|
5
|
+
desc "Seed a CSV file using the CsvAdapter and a converter"
|
6
6
|
task :csv => :environment do
|
7
|
+
converter = ENV["converter"] || ENV["CONVERTER"]
|
8
|
+
converter_options = ENV["converter_options"] || ENV["CONVERTER_OPTIONS"]
|
9
|
+
file = ENV["file"] || ENV["FILE"]
|
10
|
+
if file && converter
|
11
|
+
options = {converter: converter}
|
12
|
+
options[:converter_options] = converter_options if converter_options
|
13
|
+
Rseed::from_csv file, options
|
14
|
+
else
|
15
|
+
puts "You must specify file=<file> and converter=<converter>".red
|
16
|
+
end
|
7
17
|
end
|
8
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rseed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Monagle
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -88,8 +88,8 @@ files:
|
|
88
88
|
- lib/rseed/converter.rb
|
89
89
|
- lib/rseed/csv_adapter.rb
|
90
90
|
- lib/rseed/hash_adapter.rb
|
91
|
+
- lib/rseed/helpers.rb
|
91
92
|
- lib/rseed/processor.rb
|
92
|
-
- lib/rseed/utilities.rb
|
93
93
|
- lib/rseed/version.rb
|
94
94
|
- lib/tasks/rseed.rake
|
95
95
|
- rseed.gemspec
|