legacy_migrations 0.2.3 → 0.2.4

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.
data/README CHANGED
@@ -40,5 +40,8 @@ OR, copy all columns with the same name
40
40
  match_same_name_attributes
41
41
  end
42
42
 
43
+ Here's a slightly more thorough blog post about it:
44
+
45
+ http://frontended.com/?p=89
43
46
 
44
47
  Copyright (c) 2010 Bernie Telles, released under the MIT license
data/VERSION CHANGED
@@ -1 +1,5 @@
1
- 0.2.3
1
+ <<<<<<< HEAD
2
+ 0.2.4
3
+ =======
4
+ 0.3.5
5
+ >>>>>>> 55c756bc1f14cf99fb577c11c32db7fb293dc12e
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{legacy_migrations}
8
- s.version = "0.2.3"
8
+ s.version = "0.2.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Bernie Telles"]
12
- s.date = %q{2010-03-17}
12
+ s.date = %q{2010-08-25}
13
13
  s.description = %q{Rails plugin for transferring or updating data between two db structures.}
14
14
  s.email = %q{bernardo.telles@dms.myflorida.com}
15
15
  s.extra_rdoc_files = [
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
  "install.rb",
26
26
  "legacy_migrations.gemspec",
27
27
  "lib/legacy_migrations.rb",
28
+ "lib/legacy_migrations/future_storage.rb",
28
29
  "lib/legacy_migrations/row_matchers.rb",
29
30
  "lib/legacy_migrations/source_iterators.rb",
30
31
  "lib/legacy_migrations/squirrel.rb",
@@ -46,7 +47,7 @@ Gem::Specification.new do |s|
46
47
  s.homepage = %q{http://github.com/btelles/legacy_migrations}
47
48
  s.rdoc_options = ["--charset=UTF-8"]
48
49
  s.require_paths = ["lib"]
49
- s.rubygems_version = %q{1.3.5}
50
+ s.rubygems_version = %q{1.3.7}
50
51
  s.summary = %q{Rails plugin for transferring or updating data between two db structures.}
51
52
  s.test_files = [
52
53
  "spec/legacy_migrations_spec.rb",
@@ -60,7 +61,7 @@ Gem::Specification.new do |s|
60
61
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
61
62
  s.specification_version = 3
62
63
 
63
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
64
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
65
  s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
65
66
  else
66
67
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
@@ -0,0 +1,5 @@
1
+ module LegacyMigrations
2
+ class FutureStorage < Hash
3
+ include Singleton
4
+ end
5
+ end
@@ -12,7 +12,7 @@ module LegacyMigrations
12
12
  rows_processed += 1
13
13
  break if rows_processed == limit[:limit].to_i
14
14
  end
15
- FasterCSV::Table.new(fewer_rows)
15
+ CSV::Table.new(fewer_rows)
16
16
  else
17
17
  @from_table
18
18
  end
@@ -69,6 +69,24 @@ module LegacyMigrations
69
69
  end
70
70
  end
71
71
 
72
+ def stored(stored_attribute, *args)
73
+ options = args.extract_options!
74
+
75
+ if options[:if]
76
+ if_method = Proc.new {|record| send(options[:if], record)}
77
+ else
78
+ if_method = Proc.new {|record| true }
79
+ end
80
+ custom_method = Proc.new {|record|
81
+ if if_method.call(record)
82
+ FutureStorage.instance[@from_table.to_s][record.id][stored_attribute]
83
+ else
84
+ nil
85
+ end
86
+ }
87
+ @columns.merge!({options[:to] => custom_method})
88
+ end
89
+
72
90
  private
73
91
 
74
92
  def columns_from_options(columns, options)
@@ -1,5 +1,8 @@
1
1
  require 'legacy_migrations/transformations'
2
+ require 'legacy_migrations/future_storage'
2
3
  require 'legacy_migrations/squirrel'
4
+ require 'legacy_migrations/source_iterators'
5
+ require 'legacy_migrations/row_matchers'
3
6
  module LegacyMigrations
4
7
 
5
8
  # Define a source and destination table to transfer data
@@ -11,15 +14,31 @@ module LegacyMigrations
11
14
  # This is useful when you're trying to find faulty data in the source
12
15
  # table, and don't want to run the entire dataset.
13
16
  # * <tt>:validate</tt> - Default = true. Use ActiveRecord validations
14
- # when saving destination data.
17
+ # when saving the destination rows.
15
18
  # * <tt>:source_type</tt> - Default = :active_record. Sets the source
16
19
  # destination type.
17
20
  # Options:
18
- # _:active_record_: Assumes the From option is a class that inherits
21
+ # _:active\_record_: Assumes the From option is a class that inherits
19
22
  # from ActiveRecord::Base, then iterates through each record of From
20
23
  # table by using *From*.all.each...
21
24
  # _:other_: Assumes the From option is an iterable 'collection' whose
22
- # elements/items can respond to all columns speficied in the given block.
25
+ # elements/items can respond to all source methods speficied in the given block.
26
+ # * <tt>:store_as</tt> - Stores the generated destination record as a key
27
+ # that is retrievable in other transformations. Note that for this to work, the
28
+ # source object must respond to the method <tt>id</tt> which returns
29
+ # a value that is unique for all rows within the table (usually
30
+ # this is just a primary key).
31
+ #
32
+ # _Example_
33
+ # <tt>
34
+ # transfer_from Mammal, :to => Species, :store_as => 'new_animal' do
35
+ # match_same_name_attributes
36
+ # end
37
+ #
38
+ # transfer_from Person, :to => Animal do
39
+ # stored 'new_animal', :to => :species
40
+ # end
41
+ # </tt>
23
42
  #
24
43
  def transfer_from(from_table, *args, &block)
25
44
 
@@ -36,6 +55,52 @@ module LegacyMigrations
36
55
  @status_report
37
56
  end
38
57
 
58
+ # Define a source and destination table with which to update data
59
+ #
60
+ # This method accepts all of the same options as <tt>transfer_from</tt>.
61
+ #
62
+ # In addition, you'll need to use a series of columns that match data
63
+ # from the source to the destination. For example, if your source and
64
+ # destination data have a social security number, then you'd use the
65
+ # social security number to match records from the two rows. The following
66
+ # is how you would do that.
67
+ #
68
+ # <tt>
69
+ # update_from SourceTable, :to => DestinationTable do
70
+ # based_on do
71
+ # ssn == from.social_security_number
72
+ # end
73
+ #
74
+ # from :last_name, :to => :last_name
75
+ # end
76
+ # </tt>
77
+ #
78
+ # Note that when using the 'based_on' method, the left-hannd item always
79
+ # corresponds to a column method on the destination table.
80
+ #
81
+ # The methods available in the based_on block correspond to the well-known
82
+ # squirrel plugin's syntax. Here's a quick review of the possible operators:
83
+ #
84
+ # Handles comparisons in the query. This class is analagous to the columns in the database.
85
+ # When comparing the Condition to a value, the operators are used as follows:
86
+ # * ==, === : Straight-up Equals. Can also be used as the "IN" operator if the operand is an Array.
87
+ # Additionally, when the oprand is +nil+, the comparison is correctly generates as "IS NULL"."
88
+ # * =~ : The LIKE and REGEXP operators. If the operand is a String, it will generate a LIKE
89
+ # comparison. If it is a Regexp, the REGEXP operator will be used. NOTE: MySQL regular expressions
90
+ # are NOT the same as Ruby regular expressions. Also NOTE: No wildcards are inserted into the LIKE
91
+ # comparison, so you may add them where you wish.
92
+ # * <=> : Performs a BETWEEN comparison, as long as the operand responds to both #first and #last,
93
+ # which both Ranges and Arrays do.
94
+ # * > : A simple greater-than comparison.
95
+ # * >= : Greater-than or equal-to.
96
+ # * < : A simple less-than comparison.
97
+ # * <= : Less-than or equal-to.
98
+ # * contains? : Like =~, except automatically surrounds the operand in %s, which =~ does not do.
99
+ # * nil? : Works exactly like "column == nil", but in a nicer syntax, which is what Squirrel is all about.
100
+ #
101
+ # ==== Options
102
+ #
103
+ #
39
104
  def update_from(from_table, *args, &block)
40
105
 
41
106
  configure_transfer(from_table, *args) { yield }
@@ -59,6 +124,7 @@ module LegacyMigrations
59
124
  to_record.save(false)
60
125
  @current_operation.record_update(to_record)
61
126
  end
127
+ store_as(to_record, from_record)
62
128
  end
63
129
  else
64
130
  new_destination_record(from_record)
@@ -68,22 +134,35 @@ module LegacyMigrations
68
134
  end
69
135
 
70
136
  private
71
-
137
+
72
138
  def configure_transfer(from_table, *args, &block)
73
139
  @columns = {}
74
140
 
75
141
  @options = {:validate => true}.merge(args.extract_options!)
76
142
 
143
+
77
144
  @from_table = from_table
78
145
  @to_table = @options[:to]
146
+
147
+ add_storage_attribute if @options[:store_as].present?
148
+
79
149
  @status_report = StatusReport.instance
80
150
 
81
151
  yield
82
152
 
83
- @limit = @options[:limit] ? {:limit, @options[:limit]} : {}
153
+ @limit = @options[:limit] ? {:limit => @options[:limit]} : {}
84
154
  @type = @options[:source_type] ? @options[:source_type] : :active_record
85
155
  end
86
156
 
157
+ def add_storage_attribute
158
+ symbol_attr = @options[:store_as].to_sym
159
+ @from_table.first.class.class_eval do
160
+ define_method(symbol_attr) do
161
+ FutureStorage.instance[self.class.to_s][self.id][symbol_attr]
162
+ end
163
+ end
164
+ end
165
+
87
166
  def new_destination_record(from_record)
88
167
  columns = @columns.inject({}) do |result, attributes|
89
168
  result[attributes[0]]= attributes[1].call(from_record)
@@ -97,6 +176,16 @@ module LegacyMigrations
97
176
  new_record.save(false)
98
177
  @current_operation.record_insert(new_record)
99
178
  end
179
+ store_as(new_record, from_record)
180
+ end
181
+
182
+ def store_as(new_record, from_record)
183
+ if @options[:store_as].present?
184
+ @future_storage = FutureStorage.instance
185
+ @future_storage[@from_table.to_s] ||= {}
186
+ @future_storage[@from_table.to_s][from_record.id] ||= {}
187
+ @future_storage[@from_table.to_s][from_record.id].merge!({@options[:store_as].to_sym => new_record})
188
+ end
100
189
  end
101
190
 
102
191
  def report_validation_errors(new_record, from_record, type = 'insert')
@@ -106,6 +195,7 @@ module LegacyMigrations
106
195
  puts @current_operation.add_validation_error(new_record, from_record).pretty_output
107
196
  end
108
197
  end
198
+
109
199
  end
110
200
  include LegacyMigrations
111
201
  include LegacyMigrations::Transformations
data/spec/db/schema.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  create_table :people, :force => true do |t|
4
4
  t.string :name
5
5
  t.string :not_name
6
+ t.string :city
6
7
  t.integer :age
7
8
  end
8
9
 
@@ -11,11 +12,16 @@
11
12
  t.string :not_name
12
13
  t.integer :age
13
14
  t.string :first_name
15
+ t.integer :city_id
14
16
  end
15
17
 
16
18
  create_table :sex, :force => true do |t|
17
19
  t.string :name
18
20
  end
19
21
 
22
+ create_table :cities, :force => true do |t|
23
+ t.string :name
24
+ end
25
+
20
26
  end
21
27
 
@@ -27,7 +27,7 @@ describe LegacyMigrations do
27
27
  end
28
28
  it "accepts a CSV file" do
29
29
  person = "a simple name,age\nalbert,123"
30
- person_csv = FasterCSV.parse(person, :headers => :first_row)
30
+ person_csv = CSV.parse(person, :headers => :first_row)
31
31
  transfer_from person_csv, :to => Animal, :source_type => :csv do
32
32
  from 'a simple name', :to => :name
33
33
  end
@@ -35,7 +35,7 @@ describe LegacyMigrations do
35
35
  end
36
36
  it "limits a CSV file" do
37
37
  person = "a simple name,age\nalbert,123\nsmith,54"
38
- person_csv = FasterCSV.parse(person, :headers => :first_row)
38
+ person_csv = CSV.parse(person, :headers => :first_row)
39
39
  transfer_from person_csv, :to => Animal, :source_type => :csv, :limit => 1 do
40
40
  from 'a simple name', :to => :name
41
41
  end
@@ -55,7 +55,7 @@ describe LegacyMigrations do
55
55
  end
56
56
  it "rewinds a CSV source" do
57
57
  person = "name,age\nalbert,123\nsmith,54"
58
- person_csv = FasterCSV.parse(person, :headers => :first_row)
58
+ person_csv = CSV.parse(person, :headers => :first_row)
59
59
  transfer_from person_csv, :to => Animal, :source_type => :csv do
60
60
  from :name, :to => :name
61
61
  end
@@ -1,6 +1,7 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper.rb'))
2
2
 
3
3
  describe "transformations" do
4
+ require 'ruby-debug'
4
5
  describe 'from' do
5
6
  it "transfers attributes, given the two names" do
6
7
  Person.create(:name => 'my first name')
@@ -64,5 +65,20 @@ describe "transformations" do
64
65
  animal.not_name.should == nil
65
66
  end
66
67
  end
68
+ describe "store_as" do
69
+ it "stores a given row for future reference by another transfer or update function" do
70
+ Person.create(:name => 'choose_me', :not_name => 'not_this_one', :city => 'tally')
71
+ transfer_from Person, :to => City, :store_as => 'my_city' do
72
+ from :city, :to => :name
73
+ end
74
+ transfer_from Person, :to => Animal do
75
+ match_same_name_attributes
76
+ stored :my_city, :to => :city
77
+ end
78
+
79
+ animal = Animal.first
80
+ animal.city.name.should == 'tally'
81
+ end
82
+ end
67
83
  end
68
84
  end
data/spec/models.rb CHANGED
@@ -6,9 +6,14 @@ end
6
6
 
7
7
  class Animal < ActiveRecord::Base
8
8
  validates_format_of :name, :with => /^(\D)*$/, :allow_nil => true
9
+ belongs_to :city
9
10
  #name
10
11
  #not_name
11
12
  #first_name
12
13
  #age
13
14
  end
15
+
16
+ class City < ActiveRecord::Base
17
+ has_many :animals
18
+ end
14
19
  require 'legacy_migrations'
data/spec/spec_helper.rb CHANGED
@@ -11,7 +11,7 @@ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
11
11
  plugin_spec_dir = File.dirname(__FILE__)
12
12
 
13
13
  load(File.join(plugin_spec_dir, "db", "schema.rb"))
14
- require 'fastercsv'
14
+ require 'csv'
15
15
  require 'models'
16
16
 
17
17
  # == Fixtures
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legacy_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 4
10
+ version: 0.2.4
5
11
  platform: ruby
6
12
  authors:
7
13
  - Bernie Telles
@@ -9,19 +15,25 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-03-17 00:00:00 -04:00
18
+ date: 2010-08-25 00:00:00 -04:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
22
  name: rspec
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
20
26
  requirements:
21
27
  - - ">="
22
28
  - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 2
33
+ - 9
23
34
  version: 1.2.9
24
- version:
35
+ type: :development
36
+ version_requirements: *id001
25
37
  description: Rails plugin for transferring or updating data between two db structures.
26
38
  email: bernardo.telles@dms.myflorida.com
27
39
  executables: []
@@ -40,6 +52,7 @@ files:
40
52
  - install.rb
41
53
  - legacy_migrations.gemspec
42
54
  - lib/legacy_migrations.rb
55
+ - lib/legacy_migrations/future_storage.rb
43
56
  - lib/legacy_migrations/row_matchers.rb
44
57
  - lib/legacy_migrations/source_iterators.rb
45
58
  - lib/legacy_migrations/squirrel.rb
@@ -67,21 +80,27 @@ rdoc_options:
67
80
  require_paths:
68
81
  - lib
69
82
  required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
70
84
  requirements:
71
85
  - - ">="
72
86
  - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
73
90
  version: "0"
74
- version:
75
91
  required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
76
93
  requirements:
77
94
  - - ">="
78
95
  - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
79
99
  version: "0"
80
- version:
81
100
  requirements: []
82
101
 
83
102
  rubyforge_project:
84
- rubygems_version: 1.3.5
103
+ rubygems_version: 1.3.7
85
104
  signing_key:
86
105
  specification_version: 3
87
106
  summary: Rails plugin for transferring or updating data between two db structures.