transfer 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/README.md +172 -0
- data/Rakefile +1 -0
- data/lib/transfer/config.rb +34 -0
- data/lib/transfer/generators/active_record.rb +31 -0
- data/lib/transfer/generators/base.rb +26 -0
- data/lib/transfer/generators/mongoid.rb +28 -0
- data/lib/transfer/generators/sequel.rb +47 -0
- data/lib/transfer/transferer.rb +90 -0
- data/lib/transfer/version.rb +3 -0
- data/lib/transfer.rb +50 -0
- data/spec/fabricators/user_fabricator.rb +9 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/support/active_record.rb +42 -0
- data/spec/support/mongoid.rb +38 -0
- data/spec/support/sequel.rb +45 -0
- data/spec/support/shared_examples_for_generators.rb +64 -0
- data/spec/support/shared_examples_for_transfer.rb +154 -0
- data/spec/support/source.rb +26 -0
- data/spec/transfer/config_spec.rb +116 -0
- data/spec/transfer/generators/active_record_spec.rb +13 -0
- data/spec/transfer/generators/mongoid_spec.rb +13 -0
- data/spec/transfer/generators/sequel_spec.rb +13 -0
- data/spec/transfer/transferer_spec.rb +123 -0
- data/spec/transfer_spec.rb +174 -0
- data/transfer.gemspec +30 -0
- metadata +161 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
## Transfer Gem
|
2
|
+
Transfer data from source database to ActiveRecord, SequelModel or Mongoid models.
|
3
|
+
|
4
|
+
### Installation
|
5
|
+
Include Transfer in Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'transfer'
|
9
|
+
```
|
10
|
+
|
11
|
+
You can run bundle from command line:
|
12
|
+
|
13
|
+
```console
|
14
|
+
bundle install
|
15
|
+
```
|
16
|
+
|
17
|
+
|
18
|
+
### Compatibility
|
19
|
+
Source database: all, supported by [Sequel](http://sequel.rubyforge.org/documentation.html).
|
20
|
+
|
21
|
+
Destination: ActiveRecord, SequelModel, Mongoid.
|
22
|
+
|
23
|
+
|
24
|
+
## Configure
|
25
|
+
Set connection options of source database and global parameters:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
Transfer.configure do |c|
|
29
|
+
c.host = "localhost"
|
30
|
+
c.adapter = "postgres"
|
31
|
+
c.database = "source_database"
|
32
|
+
c.user = "username"
|
33
|
+
c.password = "password"
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
Available options:
|
38
|
+
|
39
|
+
* `validate` on/off model validations. Values: `true` or `false`, default is `false`.
|
40
|
+
* `failure_strategy` sets strategy if save of model is not successfully. Values: `:ignore` or `:rollback`, defult is `:ignore`.
|
41
|
+
* `before` [global callback](#global_callbacks).
|
42
|
+
* `success` [global callback](#global_callbacks).
|
43
|
+
* `failure` [global callback](#global_callbacks).
|
44
|
+
* `after` [global callback](#global_callbacks).
|
45
|
+
* another options interpreted as [Sequel database connection options](http://sequel.rubyforge.org/rdoc/files/doc/opening_databases_rdoc.html).
|
46
|
+
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
Direct transfer from source table `:users` to `User` model. All columns, including protected, existing in source table and destination model, will transferred:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
transfer :users => User
|
53
|
+
```
|
54
|
+
|
55
|
+
Filling the field `country` a constant value:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
transfer :users => User do
|
59
|
+
country "England"
|
60
|
+
end
|
61
|
+
```
|
62
|
+
Transfer `:name` column from source table `:users` into `first_name` of `User` model:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
transfer :users => User do
|
66
|
+
first_name :name
|
67
|
+
end
|
68
|
+
```
|
69
|
+
To produce, dynamic value (e.g. `dist_name` field), you can pass a block and access the row of source table:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
transfer :users => User do
|
73
|
+
dist_name {|row| "Mr. #{row[:first_name]}"}
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
### Global callbacks <a name="global_callbacks"/>
|
78
|
+
This callbacks called for each `transfer`.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
Transfer.configure do |c|
|
82
|
+
c.before do |klass, dataset|
|
83
|
+
#...
|
84
|
+
end
|
85
|
+
c.success do |model, row|
|
86
|
+
#...
|
87
|
+
end
|
88
|
+
c.failure do |model, row, exception|
|
89
|
+
#...
|
90
|
+
end
|
91
|
+
c.after do |klass, dataset|
|
92
|
+
#...
|
93
|
+
end
|
94
|
+
end
|
95
|
+
```
|
96
|
+
Available global callbacks:
|
97
|
+
|
98
|
+
* `before` called before an transfer started. Parameters: `klass`, `dataset`.
|
99
|
+
* `success` called if save model is successfully. Parameters: `model`, `row`.
|
100
|
+
* `failure` called if save model is not successfully. Parameters: `row`, `exception`.
|
101
|
+
* `after` called after an transfer finished. Parameters: `klass`, `dataset`.
|
102
|
+
|
103
|
+
Description of parameters:
|
104
|
+
|
105
|
+
* `dataset` source table dataset, instance of [Sequel::Dataset](http://sequel.rubyforge.org/rdoc/classes/Sequel/Dataset.html).
|
106
|
+
* `klass` is destination class.
|
107
|
+
* `model` builded model, instance of `klass`.
|
108
|
+
* `row` of source table. Type: `Hash`.
|
109
|
+
* `exception` if save of model is not successfull.
|
110
|
+
|
111
|
+
|
112
|
+
### Local transfer callbacks
|
113
|
+
You can specify callbacks in your `transfer` that are separate from the model callbacks. This callbacks called in model context, therefore `self` keyword points to model.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
transfer :users => User do
|
117
|
+
before_save do |row|
|
118
|
+
self.messages << Message.build(:title => "Transfer", :description => "Welcome to new site, #{row[:fname]}!")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
Available callbacks:
|
123
|
+
|
124
|
+
* `before_save` called before save model. Paramaters: `row`.
|
125
|
+
* `after_save` called after successfully save model. Parameters: `row`.
|
126
|
+
|
127
|
+
where `row` is row of source table, type: `Hash`.
|
128
|
+
|
129
|
+
|
130
|
+
### Filter columns
|
131
|
+
`only` filter passes source columns, specified in parameters.
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
transfer :users => User do
|
135
|
+
only :name
|
136
|
+
end
|
137
|
+
```
|
138
|
+
`except` filter passes all source columns, except for those that are specified in the parameters:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
transfer :users => User do
|
142
|
+
except :name
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
|
147
|
+
### Replace global options
|
148
|
+
Global options can be replaced global options, if it passed to `transfer`.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
transfer :users => User, :validate => false, :failure_strategy => :rollback
|
152
|
+
```
|
153
|
+
Available options for replace:
|
154
|
+
|
155
|
+
* `validate`
|
156
|
+
* `failure_strategy`
|
157
|
+
|
158
|
+
|
159
|
+
### Logging
|
160
|
+
If you also want see progress of transfer in console, use e.g. [progressbar gem](https://github.com/peleteiro/progressbar) with global callbacks.
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
require 'progressbar'
|
164
|
+
|
165
|
+
Transfer.configure do |c|
|
166
|
+
c.before {|klass, dataset| @pbar = ProgressBar.new(klass, dataset.count) }
|
167
|
+
c.success { @pbar.inc }
|
168
|
+
c.after { @pbar.halt }
|
169
|
+
end
|
170
|
+
|
171
|
+
transfer :users => User
|
172
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
module Transfer
|
4
|
+
class Config
|
5
|
+
def initialize data = {}
|
6
|
+
data.each do |key, value|
|
7
|
+
send "#{key}=", value
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def connection_options
|
12
|
+
@connection_options ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_options
|
16
|
+
@process_options ||= { :validate => true, :failure_strategy => :ignore }
|
17
|
+
end
|
18
|
+
|
19
|
+
def connection
|
20
|
+
@connection ||= Sequel.connect connection_options
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing name, *args, &block
|
24
|
+
/^((validate|failure_strategy|failure|before|after|success)|\w+)(=)?$/.match name
|
25
|
+
opt = $2 ? process_options : connection_options
|
26
|
+
if block_given?
|
27
|
+
opt[$1.to_sym] = block
|
28
|
+
else
|
29
|
+
$3 ? opt[$1.to_sym] = args[0] : opt[name]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Transfer::Generators::ActiveRecord < Transfer::Generators::Base
|
2
|
+
|
3
|
+
def self.supports? klass
|
4
|
+
defined?(ActiveRecord) && klass.ancestors.include?(ActiveRecord::Base)
|
5
|
+
end
|
6
|
+
|
7
|
+
def column_present? name
|
8
|
+
super(name) || klass.column_names.include?(name.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
def transaction &block
|
12
|
+
klass.transaction &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def create attributes, row, options={}, callbacks={}
|
16
|
+
model = klass.new attributes, :without_protection => true
|
17
|
+
|
18
|
+
model.instance_exec(row, &callbacks[:before_save]) if callbacks[:before_save]
|
19
|
+
save_options = options.select{|key| key == :validate }
|
20
|
+
|
21
|
+
model.save! save_options
|
22
|
+
options[:success].call(model, row) if options[:success]
|
23
|
+
model.instance_exec(row, &callbacks[:after_save]) if callbacks[:after_save]
|
24
|
+
model
|
25
|
+
rescue Exception => e
|
26
|
+
options[:failure].call(model, row, e) if options[:failure]
|
27
|
+
raise ActiveRecord::Rollback if options[:failure_strategy] == :rollback
|
28
|
+
model
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Transfer::Generators::Base
|
2
|
+
attr_accessor :klass
|
3
|
+
|
4
|
+
def self.supports? klass
|
5
|
+
raise "Not Yet Implemented!"
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize klass
|
9
|
+
@klass = klass
|
10
|
+
end
|
11
|
+
|
12
|
+
def before
|
13
|
+
end
|
14
|
+
|
15
|
+
def after
|
16
|
+
end
|
17
|
+
|
18
|
+
def create attributes, row, options={}, callbacks={}
|
19
|
+
raise "Not Yet Implemented!"
|
20
|
+
end
|
21
|
+
|
22
|
+
def column_present? name
|
23
|
+
klass.method_defined? name
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Transfer::Generators::Mongoid < Transfer::Generators::Base
|
2
|
+
|
3
|
+
def self.supports? klass
|
4
|
+
defined?(Mongoid) && klass.ancestors.include?(Mongoid::Document)
|
5
|
+
end
|
6
|
+
|
7
|
+
def transaction
|
8
|
+
yield
|
9
|
+
rescue
|
10
|
+
klass.delete_all
|
11
|
+
end
|
12
|
+
|
13
|
+
def create attributes, row, options={}, callbacks={}
|
14
|
+
model = klass.new attributes, :without_protection => true
|
15
|
+
model.instance_exec(row, &callbacks[:before_save]) if callbacks[:before_save]
|
16
|
+
save_options = options.select{|key| key == :validate }
|
17
|
+
model.save! save_options
|
18
|
+
|
19
|
+
options[:success].call(model, row) if options[:success]
|
20
|
+
model.instance_exec(row, &callbacks[:after_save]) if callbacks[:after_save]
|
21
|
+
model
|
22
|
+
rescue Exception => e
|
23
|
+
options[:failure].call(model, row, e) if options[:failure]
|
24
|
+
raise if options[:failure_strategy] == :rollback
|
25
|
+
model
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Transfer::Generators::Sequel < Transfer::Generators::Base
|
2
|
+
|
3
|
+
def self.supports? klass
|
4
|
+
defined?(Sequel) && klass.ancestors.include?(Sequel::Model)
|
5
|
+
end
|
6
|
+
|
7
|
+
def before
|
8
|
+
klass.unrestrict_primary_key
|
9
|
+
@strict_param_setting = klass.strict_param_setting
|
10
|
+
klass.strict_param_setting = false
|
11
|
+
klass.set_restricted_columns if @restricted_columns = klass.restricted_columns
|
12
|
+
klass.set_allowed_columns if @allowed_columns = klass.allowed_columns
|
13
|
+
end
|
14
|
+
|
15
|
+
def after
|
16
|
+
klass.restrict_primary_key
|
17
|
+
klass.strict_param_setting = @strict_param_setting
|
18
|
+
klass.set_restricted_columns *@restricted_columns if @restricted_columns
|
19
|
+
klass.set_allowed_columns *@allowed_columns if @allowed_columns
|
20
|
+
end
|
21
|
+
|
22
|
+
def column_present? name
|
23
|
+
super name || klass.columns.include?(name.to_sym)
|
24
|
+
end
|
25
|
+
|
26
|
+
def transaction &block
|
27
|
+
klass.db.transaction :savepoint => true, &block
|
28
|
+
end
|
29
|
+
|
30
|
+
def create attributes, row, options={}, callbacks={}
|
31
|
+
model = klass.new attributes
|
32
|
+
model.instance_exec(row, &callbacks[:before_save]) if callbacks[:before_save]
|
33
|
+
|
34
|
+
save_options = options.select{|key| key == :validate }
|
35
|
+
save_options[:raise_on_failure] = true
|
36
|
+
|
37
|
+
model.save save_options
|
38
|
+
model.instance_exec(row, &callbacks[:after_save]) if callbacks[:after_save]
|
39
|
+
options[:success].call(model, row) if options[:success]
|
40
|
+
model
|
41
|
+
rescue Exception => e
|
42
|
+
options[:failure].call(model, row, e) if options[:failure]
|
43
|
+
raise Sequel::Rollback if options[:failure_strategy] == :rollback
|
44
|
+
model
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class Transfer::Transferer
|
2
|
+
attr_reader :dataset, :klass
|
3
|
+
|
4
|
+
def initialize dataset, klass, &block
|
5
|
+
@dataset = dataset
|
6
|
+
@klass = klass
|
7
|
+
block.arity == 1 ? yield(self) : instance_eval(&block) if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def columns
|
11
|
+
@columns ||= dataset.columns.each_with_object({}){|i,hash| hash[i]=i if generator.column_present? i }
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_attributes source
|
15
|
+
attrs = {}
|
16
|
+
columns.each do |name, value|
|
17
|
+
attrs[name] = case value
|
18
|
+
when Proc
|
19
|
+
value.call source
|
20
|
+
when Symbol
|
21
|
+
source[value]
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
attrs
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing symbol, *args, &block
|
30
|
+
add_column symbol, block_given? ? block : args[0]
|
31
|
+
end
|
32
|
+
|
33
|
+
def process options = {}
|
34
|
+
generator.before
|
35
|
+
options[:before].call(klass, dataset) if options[:before]
|
36
|
+
generator.transaction do
|
37
|
+
dataset.each do |row|
|
38
|
+
attributes = build_attributes row
|
39
|
+
generator.create attributes, row, options, callbacks
|
40
|
+
end
|
41
|
+
end
|
42
|
+
options[:after].call(klass, dataset) if options[:after]
|
43
|
+
generator.after
|
44
|
+
end
|
45
|
+
|
46
|
+
def callbacks
|
47
|
+
@callbacks ||= {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def before_save &block
|
51
|
+
callbacks[:before_save] = block
|
52
|
+
end
|
53
|
+
|
54
|
+
def after_save &block
|
55
|
+
callbacks[:after_save] = block
|
56
|
+
end
|
57
|
+
|
58
|
+
# def failure &block
|
59
|
+
# callbacks[:failure] = block
|
60
|
+
# end
|
61
|
+
|
62
|
+
def generator
|
63
|
+
@generator ||= GENERATORS.detect{|g| g.supports? klass}.new klass
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
GENERATORS = [
|
69
|
+
Transfer::Generators::Sequel,
|
70
|
+
Transfer::Generators::ActiveRecord,
|
71
|
+
Transfer::Generators::Mongoid,
|
72
|
+
Transfer::Generators::Base
|
73
|
+
]
|
74
|
+
|
75
|
+
|
76
|
+
def add_column key, value
|
77
|
+
raise ArgumentError.new("method ##{key} in class #{klass} is not defined!") unless generator.column_present?(key)
|
78
|
+
raise ArgumentError.new("source column ##{value} not exists!") if value.instance_of?(Symbol) and !dataset.columns.include?(value)
|
79
|
+
columns[key] = value
|
80
|
+
end
|
81
|
+
|
82
|
+
def only *args
|
83
|
+
columns.delete_if {|key| !args.include?(key) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def except *args
|
87
|
+
columns.delete_if {|key| args.include?(key) }
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
data/lib/transfer.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "transfer/version"
|
2
|
+
|
3
|
+
|
4
|
+
module Transfer
|
5
|
+
extend self
|
6
|
+
autoload :Config, 'transfer/config'
|
7
|
+
autoload :Transferer, 'transfer/transferer'
|
8
|
+
|
9
|
+
module Generators
|
10
|
+
autoload :Sequel, 'transfer/generators/sequel'
|
11
|
+
autoload :ActiveRecord, 'transfer/generators/active_record'
|
12
|
+
autoload :Mongoid, 'transfer/generators/mongoid'
|
13
|
+
autoload :Base, 'transfer/generators/base'
|
14
|
+
end
|
15
|
+
|
16
|
+
def configure name = :default, &block
|
17
|
+
config = Config.new
|
18
|
+
yield config if block_given?
|
19
|
+
configs[name] = config
|
20
|
+
end
|
21
|
+
|
22
|
+
def configs
|
23
|
+
@configs ||= Hash.new {|hash, key| raise "config #{key} not exists" }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def transfer *args, &block
|
29
|
+
raise ArgumentError if args.length == 0
|
30
|
+
|
31
|
+
case args[0]
|
32
|
+
when Symbol, String
|
33
|
+
args[0] = Transfer.configs[args[0].to_sym]
|
34
|
+
transfer *args, &block
|
35
|
+
when Hash
|
36
|
+
transfer :default, *args, &block
|
37
|
+
when Transfer::Config
|
38
|
+
raise ArgumentError.new("second argument should be Hash!") unless args[1].instance_of?(Hash)
|
39
|
+
config, options = args[0], args[1]
|
40
|
+
process_keys = [:validate, :failure_strategy]
|
41
|
+
process_options = config.process_options.merge options.select{|key| process_keys.include?(key) }
|
42
|
+
sources = options.select{|key| !process_keys.include?(key) }
|
43
|
+
sources.each do |key, value|
|
44
|
+
dataset = config.connection[key]
|
45
|
+
transferer = Transfer::Transferer.new dataset, value, &block
|
46
|
+
transferer.process process_options
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rr'
|
3
|
+
require 'fabrication'
|
4
|
+
require 'transfer'
|
5
|
+
require 'database_cleaner'
|
6
|
+
|
7
|
+
Dir["./spec/support/**/*.rb"].each {|f| require f}
|
8
|
+
|
9
|
+
module TransfererHelper
|
10
|
+
def save_failure entity
|
11
|
+
if entity.kind_of?(Class)
|
12
|
+
instance_of(entity).save! { raise "force exception!" }
|
13
|
+
instance_of(entity).save { raise "force exception!" }
|
14
|
+
else
|
15
|
+
stub(entity).save! { raise "force exception!" }
|
16
|
+
stub(entity).save { raise "force exception!" }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
RSpec.configure do |c|
|
22
|
+
c.mock_with :rr
|
23
|
+
c.include TransfererHelper
|
24
|
+
|
25
|
+
c.before :suite do
|
26
|
+
DatabaseCleaner[:mongoid].strategy = :truncation
|
27
|
+
DatabaseCleaner[:active_record].strategy = :transaction
|
28
|
+
end
|
29
|
+
|
30
|
+
c.around do |example|
|
31
|
+
Sequel.transaction Sequel::DATABASES, :rollback => :always do
|
32
|
+
example.run
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
c.before :each do
|
37
|
+
DatabaseCleaner.start
|
38
|
+
end
|
39
|
+
|
40
|
+
c.after :each do
|
41
|
+
DatabaseCleaner.clean
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
4
|
+
ActiveRecord::Migration.verbose = false
|
5
|
+
|
6
|
+
|
7
|
+
class ActiveRecordUserMigration < ActiveRecord::Migration
|
8
|
+
def self.up
|
9
|
+
create_table :active_record_users, :force => true do |t|
|
10
|
+
t.string :first_name
|
11
|
+
t.string :last_name
|
12
|
+
t.string :full_name
|
13
|
+
t.string :before_save_value
|
14
|
+
t.string :protected_value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.down
|
19
|
+
drop_table :active_record_users
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActiveRecordUserMigration.up
|
24
|
+
|
25
|
+
|
26
|
+
class ActiveRecordUser < ActiveRecord::Base
|
27
|
+
set_table_name "active_record_users"
|
28
|
+
attr_accessor :dynamic_value
|
29
|
+
attr_protected :protected_value
|
30
|
+
end
|
31
|
+
|
32
|
+
class ActiveRecordUserWithFalseValidation < ActiveRecord::Base
|
33
|
+
set_table_name "active_record_users"
|
34
|
+
attr_accessor :dynamic_value
|
35
|
+
attr_protected :protected_value
|
36
|
+
|
37
|
+
validate :fake
|
38
|
+
|
39
|
+
def fake
|
40
|
+
errors.add :fake, 'fake'
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
|
3
|
+
Mongoid.configure do |config|
|
4
|
+
config.master = Mongo::Connection.new.db("godfather")
|
5
|
+
end
|
6
|
+
|
7
|
+
class MongoidUser
|
8
|
+
include Mongoid::Document
|
9
|
+
|
10
|
+
field :first_name, :type => String
|
11
|
+
field :last_name, :type => String
|
12
|
+
field :full_name, :type => String
|
13
|
+
field :before_save_value, :type => String
|
14
|
+
field :protected_value, :type => String
|
15
|
+
|
16
|
+
attr_accessor :dynamic_value
|
17
|
+
attr_protected :protected_value
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
class MongoidUserWithFalseValidation
|
22
|
+
include Mongoid::Document
|
23
|
+
|
24
|
+
field :first_name, :type => String
|
25
|
+
field :last_name, :type => String
|
26
|
+
field :full_name, :type => String
|
27
|
+
field :before_save_value, :type => String
|
28
|
+
field :protected_value, :type => String
|
29
|
+
|
30
|
+
attr_accessor :dynamic_value
|
31
|
+
attr_protected :protected_value
|
32
|
+
|
33
|
+
validate :fake
|
34
|
+
|
35
|
+
def fake
|
36
|
+
errors.add :fake, 'fake'
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
Sequel.extension :migration
|
4
|
+
|
5
|
+
DESTINATION_DB = Sequel.sqlite
|
6
|
+
|
7
|
+
class SequelUserMigration < Sequel::Migration
|
8
|
+
def up
|
9
|
+
create_table! :sequel_users do
|
10
|
+
primary_key :id
|
11
|
+
String :first_name
|
12
|
+
String :last_name
|
13
|
+
String :full_name
|
14
|
+
String :before_save_value
|
15
|
+
String :protected_value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def down
|
19
|
+
drop_table :sequel_users
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
SequelUserMigration.apply DESTINATION_DB, :up
|
25
|
+
|
26
|
+
|
27
|
+
class SequelUser < Sequel::Model
|
28
|
+
attr_accessor :dynamic_value
|
29
|
+
set_restricted_columns :protected_value
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
class SequelUserWithFalseValidation < Sequel::Model(:sequel_users)
|
34
|
+
attr_accessor :dynamic_value
|
35
|
+
set_restricted_columns :protected_value
|
36
|
+
|
37
|
+
def validate
|
38
|
+
super
|
39
|
+
errors.add(:fake, 'fake error')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
SequelUser.db = DESTINATION_DB
|
45
|
+
SequelUserWithFalseValidation.db = DESTINATION_DB
|