transfer 0.0.1
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/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
|