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 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