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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ODgyMmI1OTUxYmZlMTBkOTY4M2I5ZDRmOWQ1ODA0MDNjM2M1MTFkZg==
4
+ YTljZGYyMzkyZjE0MzQyZjVhYTdiZGM5YmMwNjdiM2RmN2M3ODFkNw==
5
5
  data.tar.gz: !binary |-
6
- YzRlMzE4YzM3ZjI4MzFjMmFmZjcwYjYxODAwMmQyMTUwNGNiY2FhMA==
6
+ NWM2ZDNmNmRhMmU0NjdkNzk5YjQ2ODAxMmZiYzM2YzYzYTU4MWI0ZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NDdhYWUyMTU2MzAyN2M0MjliYzliZWI1NjY5YmI1N2ZjNzdiMjcwMzU3Njc5
10
- MjA1OTI4ZDFkZjg4MzZhN2UzZDNmYjIzYThkYzU2ZDQwNjhlZGEzNmVlMDk4
11
- Yzg2ZGJhMmQxNDg4Mzg0Y2NhZTRjYTQ0NzI1ODQxNzllNjkwNDY=
9
+ MjMxM2IyY2VkZTI4YTg4MGQzNzk2MzMzMmYxYTdlYmQ4YzI5YTgyYmQ0NjQz
10
+ ZmYyMWJhYzkwN2QzYWUzOGRmZTdiNDIwMDQ1NTljOGQ2MjJjZjY0ZjRlYjAw
11
+ MDAyMDcyZmQ2M2IyZjA1ZjY4Y2QyYjQ1N2E5MjM2ZTRmOWEyNjU=
12
12
  data.tar.gz: !binary |-
13
- NmZmNzFhYzRiMmEyYzYwNmEyMTVhYmI4OTk1MGRhNjNkMjBiYjU2ZWFlMGQz
14
- NTA1NDNiMzU1YWM1MzIyMjUyYjgwYzBjNjQyYzBlOGEzMTI2NjZlZjVmMzEy
15
- MzdkZTI5YjFhYWI4YmY4N2NhZjRmMTQxZmE2YzU5MDlmZTkxMTE=
13
+ ZmU5YWQ5ZmY1N2MwOWRlZWE0ZDI0OTNjMmEwYjAzMjE2NWI5ZjU4ZjZhOWQ4
14
+ YThkZThmYWFlNDAxNzNmZWQzMDk3Njg3NjgxZDI5YTQ3MWI0MDkyNzk1MzJk
15
+ YzE3ZGQ5MWEwNDkwNDIxY2MyYzYxNjkxODdiZTY0OTIxYjYyZGE=
data/README.rdoc CHANGED
@@ -1,7 +1,17 @@
1
1
  = Rseed
2
2
 
3
- Rseed is a replacement for rseed. There are lots of improvements in order to make it easy to create and
4
- maintain converters.
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 User
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 this is set to *true*, then the name of the attribute will be used as the model name. In
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 FILE=user.csv CONVERTER=User CONVERTER_OPTIONS="give_admin_access=true"
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/user.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.
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
- Seeding allows you to import several files through different model converters in a single command. It involves the creation of a .seed file. Each file goes on a single line and the options are separated by pipe symbols.
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
- == The Converter Class
96
+ Rseed::from_csv 'html_colors.csv', converter: :html_color, converter_options: { no_red: true }
94
97
 
95
- === Column Setup
98
+ You can always use *if* statements in this file to filter seeds by Rails.env or something similar.
96
99
 
97
- ==== Mandatory Columns
98
- ==== Column Types
100
+ === Custom Type Conversions
99
101
 
100
- === Custom Type Conversions
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 = eval(@class_name)
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
- # match_attribute = :id
34
- # <%= @model_name %> = <%= @class_name %>.where(match_attribute => values[match_attribute]).first_or_initialize
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
- # This will return false if the record fails to update, signalling a failed import
37
- <%= @model_name %>.update_attributes <%= @model_name %>_attributes(values)
56
+ # Return true signalling a successful import
57
+ true
38
58
  end
39
59
  end
@@ -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[:model] && options[:model_attribute]
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 = options[:model] == true ? self.name : options[:model]
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]
@@ -32,6 +32,7 @@ module Rseed
32
32
  end
33
33
 
34
34
  def before_deserialize
35
+ true
35
36
  end
36
37
 
37
38
  def after_deserialize
@@ -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 = 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
@@ -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
- yield :processing
32
- start_time = Time.now
33
- adapter.process do |values, meta|
34
- result = {}
35
- meta ||= {}
36
- begin
37
- if @converter.deserialize_raw(values)
38
- result[:success] = true
39
- else
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] = "Failed to convert"
42
- result[:error] = @converter.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
- # Calculate the ETA
52
- if meta[:record_count] and meta[:total_records]
53
- remaining = meta[:total_records] - meta[:record_count]
54
- tpr = (Time.now - start_time)/meta[:record_count]
55
- meta[:eta] = remaining * tpr
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
- yield :processing, result, meta
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
@@ -1,3 +1,3 @@
1
1
  module Rseed
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/rseed.rb CHANGED
@@ -7,7 +7,7 @@ require "rseed/hash_adapter"
7
7
  require "rseed/converter"
8
8
  require "rseed/processor"
9
9
  require "rseed/csv_adapter"
10
- require "rseed/utilities"
10
+ require "rseed/helpers"
11
11
 
12
12
  module Rseed
13
13
  class << self
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 "Load csv file into a model using the CSV adapter and a converter"
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.1
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-07 00:00:00.000000000 Z
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