legacy_migrations 0.2.3 → 0.2.4

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