database_transform 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5260295aa827b58fa3504b901e310810dfb36a4a
4
- data.tar.gz: f15c12e08f7b6b736af4dc86e1d97ae0b33cd394
3
+ metadata.gz: f2ae47980a59f3120da84d3ed32be29f920f491f
4
+ data.tar.gz: 44077ed24f2f94f104acddcdb581b57db714665e
5
5
  SHA512:
6
- metadata.gz: 8c1d1982ff7532f8a7d21ab1c5541aa1169830bdcf07acecf14e8f5976d0a5d40ad498e4d48d17a3f0c32781a0657802ca1f937ce1c0cf79aeb6b737d657a069
7
- data.tar.gz: 51a77ad4b478b66270af5f4e974da544a431886d49b146f154e485e1564314d37cf3c65e7dcfbb13155eb64f4a420cbfbe00f20fcf135f5f8568cf7d3aa098b0
6
+ metadata.gz: a1d3934645c04b7213a10edab6f2aba18c655c87a971492f8ca5b73729974b71f1e1ba07b2039c2cd3bd0c6b2c458acb03afb23f629ede4501818c9dee71e6b1
7
+ data.tar.gz: 1aad272a4278423cf5b90819011c24b948e89a9d98b790c1da31064b84d802bcdd0bcee5e7d656dcdceb04078a4557ac7e2901e6ae3437e77ae6affc140835eb
data/README.md CHANGED
@@ -32,7 +32,7 @@ Or install it yourself as:
32
32
 
33
33
  ## Usage
34
34
 
35
- Database Transform is built with ActiveRecord in mind. First, define a new database conndtion to the old database in
35
+ Database Transform is built with ActiveRecord in mind. First, define a new database connection to the old database in
36
36
  database.yml:
37
37
 
38
38
  ```yaml
@@ -67,46 +67,62 @@ end
67
67
 
68
68
  A summary of methods:
69
69
 
70
- - `transform_table` tells Database Transform to perform the given transform over records in the given source table.
71
- - The first argument is the table to transform. This can be a symbol, string, or an ActiveRecord model.
72
- - `to` specifies the new table to transform to. This can be a symbol, string, or an ActiveRecord model.
73
- - If either argument is a symbol or string, an ActiveRecord model is generated which allows access to the record's
74
- data.
75
- - Source models are found in the Source namespace, and can be used as the `posts.uid` column above.
76
- - Destination models are found in the Destination namespace.
77
- - In all cases, the model will have extra accessory methods:
78
- - `transform(old_primary_key)`: This takes a primary key in the source table, and returns the transformed object.
79
- This only returns a valid result after the object has been transformed.
80
- - `transformed?(old_primary_key)`: This checks if the object has been transformed.
81
- - `default_scope` allows the programmer to specify the records to transform
82
- - `primary_key` declares the name of column with the primary key. This allows later access when relations need to be
83
- mapped.
84
- - `column` declares how to transform the contents of that column from the old database to the new one.
85
- - If `to:` is omitted, then it is assumed that the transfer function is the identity function, and the column would
86
- map across as the same name.
87
- - If `null: false` is specified, the value assigned to the column (in `to`) will be checked for nullity.
88
- - A block can be provided.
89
- - If so, then the data from the old record is passed to the block as the first argument
90
- - In the context of the block, `self` refers to the new record.
91
- - `self` has an additional attribute `source_record` which refers to the old record. (TODO)
92
- - `self` has an additional attribute `schema` which refers to the transformation schema. (TODO)
93
- - `save` declares whether the new record should be saved.
94
- - `if` and `unless` accepts a block which will be evaluated to determine if the record should be saved.
95
- - `validate` will allow the record to be saved bypassing validations. This defaults to `true`.
70
+ - `transform_table` tells Database Transform to perform the given transform over records in the given source table.
71
+ - The first argument is the table to transform. This can be a symbol, string, or an ActiveRecord model.
72
+ - `to` specifies the new table to transform to. This can be a symbol, string, or an ActiveRecord model.
73
+ - If either argument is a symbol or string, an ActiveRecord model is generated which allows access to the
74
+ record's data.
75
+ - Source models are found in the Source namespace, and can be used as the `posts.uid` column above.
76
+ - Destination models are found in the Destination namespace.
77
+ - In all cases, the model will have extra accessory methods:
78
+ - `transform(old_primary_key)`: This takes a primary key in the source table, and returns the transformed
79
+ object. This only returns a valid result after the object has been transformed.
80
+ - `transformed?(old_primary_key)`: This checks if the object has been transformed.
81
+ - `default_scope` allows the programmer to specify the records to transform
82
+ - `primary_key` declares the name of column with the primary key. This allows later access when relations need to be
83
+ mapped.
84
+ - Use the `transform` and `transformed?` methods on the model to obtain the transformed object.
85
+ - `column` declares how to transform the contents of that column from the old database to the new one.
86
+ - If `to:` is omitted, then it is assumed that the transfer function is the identity function, and the column would
87
+ map across as the same name.
88
+ - If `null: false` is specified, the value assigned to the column (in `to`) will be checked for nullity.
89
+ - A block can be provided.
90
+ - If so, then the data from the old record is passed to the block as the first argument
91
+ - In the context of the block, `self` refers to the new record.
92
+ - `self` has an additional attribute `source_record` which refers to the old record.
93
+ - `self` has an additional attribute `schema` which refers to the transformation schema.
94
+ - `save` declares whether the new record should be saved.
95
+ - `if` and `unless` accepts a block which will be evaluated to determine if the record should be saved.
96
+ - `validate` will allow the record to be saved bypassing validations. This defaults to `true`.
96
97
 
97
98
  Finally, execute the Rake task:
98
99
 
99
- $ rake db:transform my_old_app
100
+ $ rake db:transform[my_old_app]
100
101
 
101
102
  And the schema (`MyOldAppSchema`) and database connection (via `my_old_app_production`) will be established for you. A
102
103
  few variants of the schema name will be checked:
103
104
 
104
- - my_old_app
105
- - my_old_app_production
105
+ - my_old_app
106
+ - my_old_app_production
106
107
 
107
108
  Only the *source* schema will be annotated to use the other connection. The *destination* schema will be used through
108
109
  the application's normal configuration (i.e. depends on the value of `ENV['RAILS_ENV']`.)
109
110
 
111
+ Additional arguments can be passed to the schema. All arguments specified in the Rake command following the schema name
112
+ will be passed to the initialiser of the schema.
113
+
114
+ ```ruby
115
+ class MyOldAppWithArgumentsSchema < DatabaseTransform::Schema
116
+ def initialize(uploads_path)
117
+ @uploads_path = uploads_path
118
+ end
119
+ end
120
+ ```
121
+
122
+ $ rake db:transform[my_old_app_with_arguments,/home/joel/server/dumps/path]
123
+
124
+ These arguments then can be used from within any transform blocks by accessing the `schema` property.
125
+
110
126
  ## Contributing
111
127
 
112
128
  1. Fork it ( https://github.com/[my-github-username]/database_transform/fork )
@@ -114,7 +114,7 @@ class DatabaseTransform::Schema
114
114
  end
115
115
  next unless unmet_dependencies.empty?
116
116
 
117
- table_config[:transform].run_transform
117
+ table_config[:transform].run_transform(self)
118
118
  transformed_this_pass << table
119
119
  end
120
120
 
@@ -66,6 +66,8 @@ class DatabaseTransform::SchemaTable
66
66
  # together with if.
67
67
  # @option options [Proc] if A proc to call. The record will be saved if the proc returns a true value. This cannot be
68
68
  # used together with unless.
69
+ # @option options [Boolean] validate Whether to run the model validations when saving the transformed record. Defaults
70
+ # to true.
69
71
  def save(options = {})
70
72
  raise ArgumentError.new('unless and if cannot be both specified') if options[:unless] && options[:if]
71
73
  raise ArgumentError.new('Cannot specify a save clause twice for the same table') if @save
@@ -74,21 +76,24 @@ class DatabaseTransform::SchemaTable
74
76
  clause = options.delete(:unless)
75
77
  options[:if] = ->(*callback_args) { !self.instance_exec(*callback_args, &clause) }
76
78
  end
79
+ options[:validate] = true unless options.has_key?(:validate)
77
80
 
78
81
  @save = options
79
82
  end
80
83
 
81
84
  # @api private
82
85
  # To be called only by Schema#run_transform
83
- def run_transform
86
+ def run_transform(schema = nil)
84
87
  before_message =
85
88
  if @destination
86
- format("-- transforming '%s' to '%s'\n", @source.table_name, @destination.table_name)
89
+ format("-- transforming '%s' to '%s'", @source.table_name, @destination.table_name)
87
90
  else
88
- format("-- transforming '%s'\n", @source.table_name)
91
+ format("-- transforming '%s'", @source.table_name)
89
92
  end
90
93
 
91
- time_block(before_message, " -> %fs\n", &method(:transform!))
94
+ time_block(before_message, " -> %fs") do
95
+ transform!(schema)
96
+ end
92
97
  end
93
98
 
94
99
  private
@@ -109,37 +114,39 @@ class DatabaseTransform::SchemaTable
109
114
  # @yield The block to time.
110
115
  def time_block(before, after, &proc)
111
116
  start = Time.now
112
- $stderr.puts(before)
113
-
114
- result = proc.call
115
-
116
- complete = Time.now - start
117
- $stderr.printf(after, complete)
118
-
119
- result
117
+ $stderr.printf("%s\n", before)
118
+
119
+ proc.call
120
+ rescue Exception
121
+ $stderr.printf("%s (error)\n", format(after, Time.now - start))
122
+ raise
123
+ else
124
+ $stderr.printf("%s\n", format(after, Time.now - start))
120
125
  end
121
126
 
122
127
  # Performs the transform with the given parameters.
123
128
  #
129
+ # @param [DatabaseTransform::Schema] schema The schema being transformed.
124
130
  # @return [Void]
125
- def transform!
131
+ def transform!(schema)
126
132
  # For each item in the old model
127
133
  default_scope = @default_scope || @source.method(:all)
128
134
  @source.instance_exec(&default_scope).each do |record|
129
- transform_record!(record)
135
+ transform_record!(schema, record)
130
136
  end
131
137
  end
132
138
 
133
139
  # Transforms one record from the source model to the destination.
134
140
  #
141
+ # @param [DatabaseTransform::Schema] schema The schema being transformed.
135
142
  # @param [ActiveRecord::Base] old The record to map.
136
143
  # @return [Void]
137
- def transform_record!(old)
144
+ def transform_record!(schema, old)
138
145
  # Instantiate a new model record
139
146
  new = @destination.new if @destination
140
147
 
141
148
  # Map the columns over
142
- transform_record_columns!(old, new)
149
+ transform_record_columns!(schema, old, new)
143
150
  return if new.nil? || new.frozen?
144
151
 
145
152
  save_transformed_record(old, new)
@@ -147,14 +154,15 @@ class DatabaseTransform::SchemaTable
147
154
 
148
155
  # Applies the column transforms over the old record to the new record.
149
156
  #
157
+ # @param [DatabaseTransform::Schema] schema The schema being transformed.
150
158
  # @param [ActiveRecord::Base] old The record to map.
151
159
  # @param [ActiveRecord::Base] new The record to map to.
152
160
  # @return [Void]
153
- def transform_record_columns!(old, new)
161
+ def transform_record_columns!(schema, old, new)
154
162
  @columns.each do |column|
155
163
  fail ArgumentError.new unless column.is_a?(Hash)
156
164
 
157
- new_value = transform_record_field!(old, new, column[:from], column[:block])
165
+ new_value = transform_record_field!(schema, old, new, column[:from], column[:block])
158
166
 
159
167
  unless new.nil?
160
168
  break if new.frozen?
@@ -167,12 +175,13 @@ class DatabaseTransform::SchemaTable
167
175
 
168
176
  # Transforms one record's field.
169
177
  #
178
+ # @param [DatabaseTransform::Schema] schema The schema being transformed.
170
179
  # @param [ActiveRecord::Base] source The source row to map the values for.
171
180
  # @param [ActiveRecord::Base] destination The destination row to map the values to.
172
181
  # @param [Array<Symbol>] from The source columns to be used to map to the destination column.
173
182
  # @param [Proc, nil] block The block to transform the source values to the destination value.
174
183
  # @return The result of applying the transform over the input values.
175
- def transform_record_field!(source, destination, from = nil, block = nil)
184
+ def transform_record_field!(schema, source, destination, from = nil, block = nil)
176
185
  # Get the old record column values (can be a block taking multiple arguments)
177
186
  new_values = from.map { |k| source.send(k) }
178
187
 
@@ -181,15 +190,29 @@ class DatabaseTransform::SchemaTable
181
190
  new_values.first
182
191
  elsif destination
183
192
  # Map the value if necessary.
184
- # TODO: Provide the schema instance to the callback.
185
- # We should ensure that the block has a way to access the schema.
186
- destination.instance_exec(*new_values, &block)
193
+ execute_transform_block!(schema, source, destination, block, new_values)
187
194
  else
188
195
  # Call the proc
189
- block.call(*new_values)
196
+ execute_transform_block!(schema, source, Object.new, block, new_values)
190
197
  end
191
198
  end
192
199
 
200
+ # Executes the given transform block.
201
+ #
202
+ # This allows the transform to be given an appropriate value of self such that it can access the old record and the schema being used.
203
+ #
204
+ # @param [DatabaseTransform::Schema] schema The schema being transformed.
205
+ # @param [ActiveRecord::Base] source_record The old record being transformed.
206
+ # @param self_ The object to use as self in the context of the block.
207
+ # @param [Proc] block The block to execute.
208
+ # @param [Array] args The arguments to give to the block.
209
+ # @return The result of applying the block.
210
+ def execute_transform_block!(schema, source_record, self_, block, args)
211
+ self_.define_singleton_method(:source_record) { source_record } unless self_.respond_to?(:source_record)
212
+ self_.define_singleton_method(:schema) { schema } unless self_.respond_to?(:schema)
213
+ self_.instance_exec(*args, &block)
214
+ end
215
+
193
216
  # Assigns the transformed value to the new record, raising an argument error if the field was declared to not be
194
217
  # nullable and the value is null.
195
218
  #
@@ -216,9 +239,9 @@ class DatabaseTransform::SchemaTable
216
239
  def save_transformed_record(old, new)
217
240
  # Save. Skip if the conditional callback is given
218
241
  return if @save && @save[:if] && !new.instance_exec(&@save[:if])
242
+ validate = @save ? @save[:validate] : nil
219
243
 
220
- # TODO: Make validation optional using the save clause.
221
- new.save!(validate: false) unless new.destroyed?
244
+ new.save!(validate: validate != false) unless new.destroyed?
222
245
  @source.memoize_transform(old.send(@primary_key), new) if @primary_key
223
246
  end
224
247
  end
@@ -1,35 +1,34 @@
1
- # This class just loads the file containing the schema definition and hands off control to the schema.
2
- class DatabaseTransform::Transform
3
- def initialize(args)
4
- @schema = args.schema_name
5
- @extra_args = args.extras
6
- run
7
- end
8
-
9
- def run
10
- import_schema
11
- schema = @schema.constantize.new
12
-
13
- ActiveRecord::Base.logger = Logger.new('log/import.log')
14
- schema.transform!
15
- end
16
-
17
- private
18
-
19
- def import_schema
20
- schema_file = @schema.underscore
21
- begin
22
- return require(File.join(Rails.root, 'db', 'transforms', schema_file))
23
- rescue LoadError
24
- end
25
-
26
- require (File.join(Rails.root, 'db', 'transforms', schema_file, schema_file))
27
- end
28
- end
29
-
30
- namespace :db do
31
- desc 'Transform old database schemas'
32
- task :transform, [:schema_name] => :environment do |_, args|
33
- DatabaseTransform::Transform.new(args)
34
- end
35
- end
1
+ # This class just loads the file containing the schema definition and hands off control to the schema.
2
+ class DatabaseTransform::Transform
3
+ def initialize(args)
4
+ @schema = args.schema_name
5
+ @argv = args.extras
6
+ end
7
+
8
+ def run
9
+ require_schema
10
+ schema = @schema.camelize.constantize.new(*@argv)
11
+
12
+ ActiveRecord::Base.logger = Logger.new('log/import.log')
13
+ schema.transform!
14
+ end
15
+
16
+ private
17
+
18
+ def require_schema
19
+ schema_file = @schema.underscore
20
+ begin
21
+ return require(File.join(Rails.root, 'db', 'transforms', schema_file))
22
+ rescue LoadError
23
+ end
24
+
25
+ require (File.join(Rails.root, 'db', 'transforms', schema_file, schema_file))
26
+ end
27
+ end
28
+
29
+ namespace :db do
30
+ desc 'Transform old database schemas'
31
+ task :transform, [:schema_name] => :environment do |_, args|
32
+ DatabaseTransform::Transform.new(args).run
33
+ end
34
+ end
@@ -1,3 +1,3 @@
1
1
  module DatabaseTransform
2
- VERSION = '0.1.1'
2
+ VERSION = '0.1.2'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: database_transform
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Low
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-28 00:00:00.000000000 Z
11
+ date: 2015-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport