rseed 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|