database_transform 0.1.1 → 0.1.2

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