kantox-split 0.2.0
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +243 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/README.md +39 -0
- data/Rakefile +28 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/kantox-split.gemspec +35 -0
- data/lib/kantox/split.rb +64 -0
- data/lib/kantox/split/adapters.rb +2 -0
- data/lib/kantox/split/adapters/getters.rb +58 -0
- data/lib/kantox/split/graph.rb +131 -0
- data/lib/kantox/split/hooker.rb +53 -0
- data/lib/kantox/split/utils.rb +62 -0
- data/lib/kantox/split/version.rb +5 -0
- metadata +161 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 3cb0f60879f9ec28ba4c9126ffc4be86b89086b9
|
|
4
|
+
data.tar.gz: b777254cd8242e5fc749e8af907e92fd7c347aea
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5054b81ed6166717e681c78bcbd78dbe0eb51b663802481ab95340aa0c4e4f765aaa262d572b686c5ef98d3d547a6af4a4a8b12b7496206e1399c1a4c027099c
|
|
7
|
+
data.tar.gz: 3084b9bc907bb1ce883d7c2c4a1c8c305898b45395fe486963ff0f7b487bd25b37c067d8043bf08d7d0d46360943cf69788aea23ac0a5fc6680977e80b68d8da
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2016-05-12 15:22:43 +0200 using RuboCop version 0.39.0.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 1
|
|
10
|
+
# Configuration parameters: AllowSafeAssignment.
|
|
11
|
+
Lint/AssignmentInCondition:
|
|
12
|
+
Exclude:
|
|
13
|
+
- 'lib/kantox/split/adapters/getters.rb'
|
|
14
|
+
|
|
15
|
+
# Offense count: 4
|
|
16
|
+
# Cop supports --auto-correct.
|
|
17
|
+
Lint/Debugger:
|
|
18
|
+
Exclude:
|
|
19
|
+
- 'lib/kantox/split/adapters/file_io.rb'
|
|
20
|
+
- 'lib/kantox/split/adapters/rethink-db.rb'
|
|
21
|
+
|
|
22
|
+
# Offense count: 1
|
|
23
|
+
# Cop supports --auto-correct.
|
|
24
|
+
# Configuration parameters: IgnoreEmptyBlocks.
|
|
25
|
+
Lint/UnusedBlockArgument:
|
|
26
|
+
Exclude:
|
|
27
|
+
- 'lib/kantox/split/graph.rb'
|
|
28
|
+
|
|
29
|
+
# Offense count: 1
|
|
30
|
+
# Cop supports --auto-correct.
|
|
31
|
+
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
|
|
32
|
+
Lint/UnusedMethodArgument:
|
|
33
|
+
Exclude:
|
|
34
|
+
- 'lib/kantox/split/graph.rb'
|
|
35
|
+
|
|
36
|
+
# Offense count: 5
|
|
37
|
+
Metrics/AbcSize:
|
|
38
|
+
Max: 33
|
|
39
|
+
|
|
40
|
+
# Offense count: 5
|
|
41
|
+
Metrics/CyclomaticComplexity:
|
|
42
|
+
Max: 9
|
|
43
|
+
|
|
44
|
+
# Offense count: 28
|
|
45
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
|
|
46
|
+
# URISchemes: http, https
|
|
47
|
+
Metrics/LineLength:
|
|
48
|
+
Max: 161
|
|
49
|
+
|
|
50
|
+
# Offense count: 1
|
|
51
|
+
# Configuration parameters: CountComments.
|
|
52
|
+
Metrics/MethodLength:
|
|
53
|
+
Max: 34
|
|
54
|
+
|
|
55
|
+
# Offense count: 2
|
|
56
|
+
Metrics/PerceivedComplexity:
|
|
57
|
+
Max: 9
|
|
58
|
+
|
|
59
|
+
# Offense count: 1
|
|
60
|
+
Style/AsciiComments:
|
|
61
|
+
Exclude:
|
|
62
|
+
- 'lib/kantox/split/adapters/getters.rb'
|
|
63
|
+
|
|
64
|
+
# Offense count: 4
|
|
65
|
+
Style/CaseEquality:
|
|
66
|
+
Exclude:
|
|
67
|
+
- 'lib/kantox/split/adapters/file_io.rb'
|
|
68
|
+
- 'lib/kantox/split/adapters/rethink-db.rb'
|
|
69
|
+
|
|
70
|
+
# Offense count: 2
|
|
71
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
|
72
|
+
# SupportedStyles: nested, compact
|
|
73
|
+
Style/ClassAndModuleChildren:
|
|
74
|
+
Exclude:
|
|
75
|
+
- 'lib/kantox/split.rb'
|
|
76
|
+
|
|
77
|
+
# Offense count: 2
|
|
78
|
+
# Cop supports --auto-correct.
|
|
79
|
+
# Configuration parameters: Keywords.
|
|
80
|
+
# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW
|
|
81
|
+
Style/CommentAnnotation:
|
|
82
|
+
Exclude:
|
|
83
|
+
- 'lib/kantox/split/graph.rb'
|
|
84
|
+
|
|
85
|
+
# Offense count: 1
|
|
86
|
+
# Cop supports --auto-correct.
|
|
87
|
+
Style/CommentIndentation:
|
|
88
|
+
Exclude:
|
|
89
|
+
- 'kantox-split.gemspec'
|
|
90
|
+
|
|
91
|
+
# Offense count: 16
|
|
92
|
+
Style/Documentation:
|
|
93
|
+
Exclude:
|
|
94
|
+
- 'spec/**/*'
|
|
95
|
+
- 'test/**/*'
|
|
96
|
+
- 'lib/kantox/split.rb'
|
|
97
|
+
- 'lib/kantox/split/adapters/file_io.rb'
|
|
98
|
+
- 'lib/kantox/split/adapters/getters.rb'
|
|
99
|
+
- 'lib/kantox/split/adapters/rethink-db.rb'
|
|
100
|
+
- 'lib/kantox/split/graph.rb'
|
|
101
|
+
- 'lib/kantox/split/hooker.rb'
|
|
102
|
+
- 'lib/kantox/split/utils.rb'
|
|
103
|
+
|
|
104
|
+
# Offense count: 1
|
|
105
|
+
Style/EachWithObject:
|
|
106
|
+
Exclude:
|
|
107
|
+
- 'lib/kantox/split/graph.rb'
|
|
108
|
+
|
|
109
|
+
# Offense count: 8
|
|
110
|
+
# Cop supports --auto-correct.
|
|
111
|
+
# Configuration parameters: AllowAdjacentOneLineDefs.
|
|
112
|
+
Style/EmptyLineBetweenDefs:
|
|
113
|
+
Exclude:
|
|
114
|
+
- 'lib/kantox/split.rb'
|
|
115
|
+
- 'lib/kantox/split/adapters/file_io.rb'
|
|
116
|
+
- 'lib/kantox/split/adapters/rethink-db.rb'
|
|
117
|
+
- 'lib/kantox/split/utils.rb'
|
|
118
|
+
|
|
119
|
+
# Offense count: 1
|
|
120
|
+
# Cop supports --auto-correct.
|
|
121
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
|
122
|
+
# SupportedStyles: empty_lines, no_empty_lines
|
|
123
|
+
Style/EmptyLinesAroundBlockBody:
|
|
124
|
+
Exclude:
|
|
125
|
+
- 'kantox-split.gemspec'
|
|
126
|
+
|
|
127
|
+
# Offense count: 1
|
|
128
|
+
# Cop supports --auto-correct.
|
|
129
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
|
130
|
+
# SupportedStyles: empty_lines, no_empty_lines
|
|
131
|
+
Style/EmptyLinesAroundModuleBody:
|
|
132
|
+
Exclude:
|
|
133
|
+
- 'lib/kantox/split/adapters/getters.rb'
|
|
134
|
+
|
|
135
|
+
# Offense count: 1
|
|
136
|
+
# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts.
|
|
137
|
+
Style/FileName:
|
|
138
|
+
Exclude:
|
|
139
|
+
- 'lib/kantox/split/adapters/rethink-db.rb'
|
|
140
|
+
|
|
141
|
+
# Offense count: 25
|
|
142
|
+
# Cop supports --auto-correct.
|
|
143
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
|
144
|
+
# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline
|
|
145
|
+
Style/MethodDefParentheses:
|
|
146
|
+
Enabled: false
|
|
147
|
+
|
|
148
|
+
# Offense count: 2
|
|
149
|
+
# Cop supports --auto-correct.
|
|
150
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
|
151
|
+
# SupportedStyles: aligned, indented
|
|
152
|
+
Style/MultilineMethodCallIndentation:
|
|
153
|
+
Enabled: false
|
|
154
|
+
|
|
155
|
+
# Offense count: 3
|
|
156
|
+
Style/MultilineTernaryOperator:
|
|
157
|
+
Exclude:
|
|
158
|
+
- 'lib/kantox/split/adapters/getters.rb'
|
|
159
|
+
- 'lib/kantox/split/utils.rb'
|
|
160
|
+
|
|
161
|
+
# Offense count: 3
|
|
162
|
+
# Cop supports --auto-correct.
|
|
163
|
+
Style/MutableConstant:
|
|
164
|
+
Exclude:
|
|
165
|
+
- 'lib/kantox/split/graph.rb'
|
|
166
|
+
- 'lib/kantox/split/hooker.rb'
|
|
167
|
+
- 'lib/kantox/split/version.rb'
|
|
168
|
+
|
|
169
|
+
# Offense count: 1
|
|
170
|
+
Style/NestedTernaryOperator:
|
|
171
|
+
Exclude:
|
|
172
|
+
- 'lib/kantox/split/adapters/getters.rb'
|
|
173
|
+
|
|
174
|
+
# Offense count: 1
|
|
175
|
+
# Cop supports --auto-correct.
|
|
176
|
+
Style/NumericLiterals:
|
|
177
|
+
MinDigits: 6
|
|
178
|
+
|
|
179
|
+
# Offense count: 3
|
|
180
|
+
# Configuration parameters: SupportedStyles.
|
|
181
|
+
# SupportedStyles: compact, exploded
|
|
182
|
+
Style/RaiseArgs:
|
|
183
|
+
EnforcedStyle: compact
|
|
184
|
+
|
|
185
|
+
# Offense count: 1
|
|
186
|
+
# Cop supports --auto-correct.
|
|
187
|
+
Style/RedundantSelf:
|
|
188
|
+
Exclude:
|
|
189
|
+
- 'lib/kantox/split/graph.rb'
|
|
190
|
+
|
|
191
|
+
# Offense count: 2
|
|
192
|
+
# Cop supports --auto-correct.
|
|
193
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
|
|
194
|
+
# SupportedStyles: slashes, percent_r, mixed
|
|
195
|
+
Style/RegexpLiteral:
|
|
196
|
+
Exclude:
|
|
197
|
+
- 'kantox-split.gemspec'
|
|
198
|
+
|
|
199
|
+
# Offense count: 6
|
|
200
|
+
# Cop supports --auto-correct.
|
|
201
|
+
Style/RescueModifier:
|
|
202
|
+
Exclude:
|
|
203
|
+
- 'lib/kantox/split.rb'
|
|
204
|
+
- 'lib/kantox/split/graph.rb'
|
|
205
|
+
- 'lib/kantox/split/utils.rb'
|
|
206
|
+
|
|
207
|
+
# Offense count: 2
|
|
208
|
+
# Cop supports --auto-correct.
|
|
209
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
|
210
|
+
# SupportedStyles: only_raise, only_fail, semantic
|
|
211
|
+
Style/SignalException:
|
|
212
|
+
Exclude:
|
|
213
|
+
- 'lib/kantox/split/adapters/getters.rb'
|
|
214
|
+
- 'lib/kantox/split/hooker.rb'
|
|
215
|
+
|
|
216
|
+
# Offense count: 2
|
|
217
|
+
# Cop supports --auto-correct.
|
|
218
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
|
219
|
+
# SupportedStyles: space, no_space
|
|
220
|
+
Style/SpaceBeforeBlockBraces:
|
|
221
|
+
Enabled: false
|
|
222
|
+
|
|
223
|
+
# Offense count: 5
|
|
224
|
+
# Cop supports --auto-correct.
|
|
225
|
+
Style/SpaceInsideBrackets:
|
|
226
|
+
Exclude:
|
|
227
|
+
- 'lib/kantox/split.rb'
|
|
228
|
+
- 'lib/kantox/split/adapters/getters.rb'
|
|
229
|
+
- 'lib/kantox/split/graph.rb'
|
|
230
|
+
|
|
231
|
+
# Offense count: 4
|
|
232
|
+
# Cop supports --auto-correct.
|
|
233
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline.
|
|
234
|
+
# SupportedStyles: single_quotes, double_quotes
|
|
235
|
+
Style/StringLiterals:
|
|
236
|
+
Enabled: false
|
|
237
|
+
|
|
238
|
+
# Offense count: 2
|
|
239
|
+
# Cop supports --auto-correct.
|
|
240
|
+
Style/WhileUntilDo:
|
|
241
|
+
Exclude:
|
|
242
|
+
- 'lib/kantox/split.rb'
|
|
243
|
+
- 'lib/kantox/split/utils.rb'
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Kantox::Split
|
|
2
|
+
|
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/kantox/split`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
4
|
+
|
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'kantox-split'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
$ bundle
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
$ gem install kantox-split
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
TODO: Write usage instructions here
|
|
26
|
+
|
|
27
|
+
## Development
|
|
28
|
+
|
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
30
|
+
|
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
32
|
+
|
|
33
|
+
## Contributing
|
|
34
|
+
|
|
35
|
+
1. Fork it ( https://github.com/[my-github-username]/kantox-split/fork )
|
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
39
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
|
|
4
|
+
require 'bundler'
|
|
5
|
+
require 'bundler/setup'
|
|
6
|
+
require 'bundler/gem_tasks'
|
|
7
|
+
|
|
8
|
+
require 'rspec/core/rake_task'
|
|
9
|
+
|
|
10
|
+
begin
|
|
11
|
+
Bundler.setup(:default, :development, :test)
|
|
12
|
+
rescue Bundler::BundlerError => e
|
|
13
|
+
$stderr.puts e.message
|
|
14
|
+
$stderr.puts 'Run `bundle install` to install missing gems'
|
|
15
|
+
exit e.status_code
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
desc 'Tests'
|
|
19
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
20
|
+
spec.rspec_opts = '-Ispec'
|
|
21
|
+
# spec.rcov = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require 'cucumber/rake/task'
|
|
25
|
+
desc 'Cucumber'
|
|
26
|
+
Cucumber::Rake::Task.new(:features)
|
|
27
|
+
|
|
28
|
+
task default: [:features, :spec]
|
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "kantox/split"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'kantox/split/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'kantox-split'
|
|
8
|
+
spec.version = Kantox::Split::VERSION
|
|
9
|
+
spec.authors = ['Kantox LTD']
|
|
10
|
+
spec.email = ['aleksei.matiushkin@kantox.com']
|
|
11
|
+
|
|
12
|
+
spec.summary = 'Easy split ActiveRecord’s CUD operations to write to separate data source.'
|
|
13
|
+
spec.description = 'This gem is extremely useful while in process of migration to another data source.'
|
|
14
|
+
spec.homepage = 'http://kantox.com'
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(/^(test|spec|features)\//) }
|
|
17
|
+
spec.bindir = 'bin'
|
|
18
|
+
spec.executables = spec.files.grep(/^exe\//) { |f| File.basename(f) }
|
|
19
|
+
spec.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
if spec.respond_to?(:metadata)
|
|
22
|
+
# spec.metadata['allowed_push_host'] = "TODO: FURY"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
spec.add_dependency 'rgl', '~> 0.5'
|
|
26
|
+
spec.add_dependency 'kungfuig', '~> 0'
|
|
27
|
+
|
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
30
|
+
|
|
31
|
+
spec.add_development_dependency 'rspec', '~> 2.12'
|
|
32
|
+
spec.add_development_dependency 'cucumber', '~> 1.3'
|
|
33
|
+
spec.add_development_dependency 'yard', '~> 0'
|
|
34
|
+
|
|
35
|
+
end
|
data/lib/kantox/split.rb
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'kantox/split/version'
|
|
2
|
+
require 'kantox/split/hooker'
|
|
3
|
+
require 'kantox/split/adapters'
|
|
4
|
+
require 'kantox/split/graph'
|
|
5
|
+
|
|
6
|
+
module Kantox
|
|
7
|
+
module Split
|
|
8
|
+
class ::ActiveRecord::Base
|
|
9
|
+
include Hooker
|
|
10
|
+
include Adapters::Getters
|
|
11
|
+
|
|
12
|
+
##########################################################################
|
|
13
|
+
#### Graph for instances
|
|
14
|
+
##########################################################################
|
|
15
|
+
include Graph::Vertex
|
|
16
|
+
configure_edges do |e|
|
|
17
|
+
e.reflections.values
|
|
18
|
+
end
|
|
19
|
+
configure_embedded :attributes
|
|
20
|
+
configure_schild %w(name caption label)
|
|
21
|
+
.flat_map { |e| [ e, "#{self.class.name.singularize.underscore}_#{e}"] }
|
|
22
|
+
.map { |field| "respond_to?(:#{field}) && #{field}" }.join(' || ') + " || ''"
|
|
23
|
+
|
|
24
|
+
##########################################################################
|
|
25
|
+
#### Graph for classes
|
|
26
|
+
##########################################################################
|
|
27
|
+
class << self
|
|
28
|
+
include Graph::Vertex
|
|
29
|
+
configure_edges do |e|
|
|
30
|
+
e.reflections.values
|
|
31
|
+
end
|
|
32
|
+
configure_schild :name
|
|
33
|
+
|
|
34
|
+
def vertices
|
|
35
|
+
edges.map do |edge|
|
|
36
|
+
next [edge.name, ::ActiveRecord::Base] if edge.options[:polymorphic]
|
|
37
|
+
|
|
38
|
+
while edge && edge.options[:through] do
|
|
39
|
+
edge = edges.detect { |e| e.name == edge.options[:through] }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
edge_singular = (edge.options[:class_name] || edge.name.to_s).singularize.camelize
|
|
43
|
+
edge_singular += 's' if edge_singular[-2..-1] == 'es' # WTF, Rails?
|
|
44
|
+
vtx = edge_singular.constantize rescue edge_singular
|
|
45
|
+
edge && [edge.name, edge.macro == :has_many ? [vtx] : vtx]
|
|
46
|
+
end.compact.to_h
|
|
47
|
+
end
|
|
48
|
+
def to_h levels = 0, collected = []
|
|
49
|
+
{ self: attribute_names.map(&:to_sym) }.merge(vertices.map do |k, v|
|
|
50
|
+
v_deep = Kantox::Split::Utils.omnivorous_to_h v, levels, collected
|
|
51
|
+
[k, v == v_deep ? v : { v => v_deep }]
|
|
52
|
+
end.to_h)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class ::ActiveRecord::Reflection::MacroReflection
|
|
58
|
+
include Graph::Edge
|
|
59
|
+
configure_vertex do |o|
|
|
60
|
+
{ macro: o.macro, name: o.name, method: o.name, options: o.options, class: o.class }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Kantox
|
|
2
|
+
module Split
|
|
3
|
+
module Adapters
|
|
4
|
+
module Getters
|
|
5
|
+
def self.included base
|
|
6
|
+
fail TypeError.new("Hooker may be included in «ActiveRecord::Base»d classes since it requires «after_commit» hook") unless base <= ::ActiveRecord::Base
|
|
7
|
+
[:has_many, :has_one, :composed_of, :belongs_to].each do |r|
|
|
8
|
+
define_method "reflections_#{r}" do
|
|
9
|
+
reflections.group_by{ |_, v| v.macro }[r]
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# According to `:for_relation`, we’ll follow the relations:
|
|
15
|
+
# :has_many :has_many
|
|
16
|
+
# :belongs_to :belongs_to
|
|
17
|
+
# :has_one none
|
|
18
|
+
def as_document for_reflection = [:has_many, :has_one, :belongs_to], deep = true, collected = []
|
|
19
|
+
this = self
|
|
20
|
+
refls = reflections.group_by{ |_, v| v.macro }
|
|
21
|
+
puts "====> REFLS: #{refls.keys}"
|
|
22
|
+
{ attributes: attributes }.merge(deep ?
|
|
23
|
+
[*for_reflection].map do |r|
|
|
24
|
+
next unless refls[r].is_a? Enumerable
|
|
25
|
+
[
|
|
26
|
+
r,
|
|
27
|
+
refls[r].map do |rr|
|
|
28
|
+
begin
|
|
29
|
+
value = this.public_send(rr.first)
|
|
30
|
+
collected << value unless already_collected = collected.include?(value)
|
|
31
|
+
deepers = {
|
|
32
|
+
has_many: [:has_one, :has_many],
|
|
33
|
+
has_one: [:has_one],
|
|
34
|
+
belongs_to: [:belongs_to]
|
|
35
|
+
}
|
|
36
|
+
[
|
|
37
|
+
rr.first,
|
|
38
|
+
already_collected || value.nil? ? value :
|
|
39
|
+
case r
|
|
40
|
+
when :has_many then value.map { |v| v.as_document(deepers[r] & for_reflection, deep, collected) }
|
|
41
|
+
when :has_one, :belongs_to then value.as_document(deepers[r] & for_reflection, deep, collected)
|
|
42
|
+
else value
|
|
43
|
+
end
|
|
44
|
+
]
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
puts "CatchedError :: #{e} :: #{r} :: #{rr}"
|
|
47
|
+
[ rr.first, "[ERR] :: #{e.message}" ]
|
|
48
|
+
end
|
|
49
|
+
end.to_h
|
|
50
|
+
]
|
|
51
|
+
end.compact.to_h
|
|
52
|
+
: {})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
require 'kantox/split/utils'
|
|
2
|
+
require 'rgl/adjacency'
|
|
3
|
+
|
|
4
|
+
module Kantox
|
|
5
|
+
module Split
|
|
6
|
+
module Graph
|
|
7
|
+
CONFIGURE_EVALUATOR = <<-EOCFGEV
|
|
8
|
+
def configure_%{entity} parameter = nil, &cb
|
|
9
|
+
store_variable :graph_%{entity}_getter, parameter || cb
|
|
10
|
+
class_eval do
|
|
11
|
+
def %{entity}
|
|
12
|
+
lookup_variable_value lookup_variable :graph_%{entity}_getter
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
EOCFGEV
|
|
17
|
+
|
|
18
|
+
##########################################################################
|
|
19
|
+
module Attributed
|
|
20
|
+
include Kantox::Split::Utils
|
|
21
|
+
module ClassMethods
|
|
22
|
+
def configure_embedded parameter = nil, &cb
|
|
23
|
+
store_variable :embedded_parameter_getter, parameter || cb
|
|
24
|
+
class_eval do
|
|
25
|
+
def embedded
|
|
26
|
+
lookup_variable_value lookup_variable :embedded_parameter_getter
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
##########################################################################
|
|
33
|
+
module Vertex
|
|
34
|
+
def self.included base
|
|
35
|
+
base.include Kantox::Split::Utils
|
|
36
|
+
base.extend Attributed::ClassMethods
|
|
37
|
+
base.include InstanceMethods
|
|
38
|
+
base.extend ClassMethods
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module InstanceMethods
|
|
42
|
+
def leaf?
|
|
43
|
+
edges.empty?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def vertices
|
|
47
|
+
return [] unless respond_to? :edges
|
|
48
|
+
edges.map do |edge|
|
|
49
|
+
edge.vertex_getter.call self rescue nil
|
|
50
|
+
end.compact
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def graph_node_id
|
|
54
|
+
label = schild if respond_to?(:schild)
|
|
55
|
+
label = label.empty? ? self.class.name : "\"#{label}\": #{self.class.name}"
|
|
56
|
+
"#{label} «#{id rescue nil}»"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def to_h levels = 0, collected = []
|
|
60
|
+
(respond_to?(:embedded) && embedded || {}).merge(
|
|
61
|
+
vertices.map do |k, v|
|
|
62
|
+
next if v.nil? || v.respond_to?(:empty?) && v.empty?
|
|
63
|
+
[k[:name], Kantox::Split::Utils.omnivorous_to_h(v, levels, collected, 'obj.respond_to?(:graph_node_id) ? obj.graph_node_id : obj.__id__')]
|
|
64
|
+
end.compact.to_h
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def to_graph levels = 0, g = RGL::DirectedAdjacencyGraph.new, root = self.graph_node_id
|
|
69
|
+
vertices.each do |k, v|
|
|
70
|
+
next if v.nil? || v.respond_to?(:empty?) && v.empty?
|
|
71
|
+
next unless v.is_a? Vertex # FIXME ARRAY
|
|
72
|
+
g.add_edge(root, vtx = v.graph_node_id)
|
|
73
|
+
v.to_graph(levels - 1, g, vtx) if levels > 0 && v.respond_to?(:to_graph)
|
|
74
|
+
end
|
|
75
|
+
g
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
module ClassMethods
|
|
80
|
+
# to be called as:
|
|
81
|
+
#
|
|
82
|
+
# class ActiveRecord::Base
|
|
83
|
+
# include Kantox::Split::Graph::Vertex
|
|
84
|
+
# configure_edges :reflections # the parameter must be Enumerable
|
|
85
|
+
# ...
|
|
86
|
+
#
|
|
87
|
+
%i(edges schild).each do |entity|
|
|
88
|
+
module_eval CONFIGURE_EVALUATOR % { entity: entity }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
##########################################################################
|
|
93
|
+
module Edge
|
|
94
|
+
def self.included base
|
|
95
|
+
base.include Kantox::Split::Utils
|
|
96
|
+
base.extend Attributed::ClassMethods
|
|
97
|
+
base.include InstanceMethods
|
|
98
|
+
base.extend ClassMethods
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
module InstanceMethods
|
|
102
|
+
def vertex_getter
|
|
103
|
+
return nil unless respond_to? :vertex
|
|
104
|
+
v = (vtx = vertex).is_a?(Hash) ? vtx[:method] || vtx[:lambda] : vtx
|
|
105
|
+
lambda do |vertex|
|
|
106
|
+
unless [:todos].include? v # FIXME UGLY HACK
|
|
107
|
+
[ vtx, Utils.lookup_variable_value(vertex, v) ]
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
module ClassMethods
|
|
114
|
+
%i(vertex).each do |entity|
|
|
115
|
+
module_eval CONFIGURE_EVALUATOR % { entity: entity }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
##########################################################################
|
|
120
|
+
def self.tree root, depth = -1
|
|
121
|
+
return nil unless root.respond_to? :vertices
|
|
122
|
+
|
|
123
|
+
root.vertices.inject({}) do |memo, v|
|
|
124
|
+
memo[v] = { vertex: v }
|
|
125
|
+
|
|
126
|
+
memo
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'kantox/split/utils'
|
|
2
|
+
|
|
3
|
+
module Kantox
|
|
4
|
+
module Split
|
|
5
|
+
module Hooker
|
|
6
|
+
ACTIONS = [:create, :update, :destroy]
|
|
7
|
+
|
|
8
|
+
def self.included base
|
|
9
|
+
fail TypeError.new("Hooker may be included in «ActiveRecord::Base»d classes since it requires «after_commit» hook") unless base <= ::ActiveRecord::Base
|
|
10
|
+
base.include Kantox::Split::Utils
|
|
11
|
+
base.include InstanceMethods
|
|
12
|
+
base.extend ClassMethods
|
|
13
|
+
|
|
14
|
+
ACTIONS.each do |action|
|
|
15
|
+
base.after_commit "hooked_action_on_#{action}", on: action
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
# @param hooks [Hash] the hash of `method: Handler`, e. g.:
|
|
21
|
+
# `{ create: CreateMapper, destroy: DestroyMapper }`.
|
|
22
|
+
# Each handler must implement contructor, receiving `ActiveRecord`,
|
|
23
|
+
# `#save`, `#update` and `#destroy` methods to store the content
|
|
24
|
+
# into splitted data store, and (optionally) `#to_hash` method,
|
|
25
|
+
# producing a hash to be splitted and stored.
|
|
26
|
+
def hook default_hooker = nil, **hooks
|
|
27
|
+
@split_hooks_on_commit ||= { hooker: default_hooker }
|
|
28
|
+
@split_hooks_on_commit.merge!(ACTIONS.map { |a| [a, default_hooker] }.to_h) if default_hooker
|
|
29
|
+
@split_hooks_on_commit.merge!(hooks)
|
|
30
|
+
if block_given?
|
|
31
|
+
yielder = Proc.new # reinstantiate &cb
|
|
32
|
+
@split_hooks_on_commit.values.uniq.each do |h|
|
|
33
|
+
h.config(&yielder) if h.respond_to? :config
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
module InstanceMethods
|
|
40
|
+
def hooks
|
|
41
|
+
lookup_variable(:split_hooks_on_commit)
|
|
42
|
+
end
|
|
43
|
+
private :hooks
|
|
44
|
+
|
|
45
|
+
ACTIONS.each do |action|
|
|
46
|
+
define_method "hooked_action_on_#{action}" do
|
|
47
|
+
hooks[action] && hooks[action].respond_to?(action) && hooks[action].public_send(action, self)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Kantox
|
|
2
|
+
module Split
|
|
3
|
+
module Utils
|
|
4
|
+
class << self
|
|
5
|
+
def store_variable object, name, value
|
|
6
|
+
# FIXME
|
|
7
|
+
object.instance_variable_set :"@#{name}", value
|
|
8
|
+
object.class.instance_variable_set :"@#{name}", value
|
|
9
|
+
end
|
|
10
|
+
def lookup_variable object, name
|
|
11
|
+
return object.instance_variable_get(:"@#{name}") if object.instance_variable_defined?(:"@#{name}")
|
|
12
|
+
klazz = object.class
|
|
13
|
+
while klazz do
|
|
14
|
+
break klazz.instance_variable_get(:"@#{name}") if klazz.instance_variable_defined?(:"@#{name}")
|
|
15
|
+
klazz = klazz.superclass
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
def lookup_variable_value object, getter
|
|
19
|
+
case getter
|
|
20
|
+
when Array then getter.map { |v| lookup_variable_value object, v }
|
|
21
|
+
when Hash then getter.map { |k, v| [k, lookup_variable_value(object, v)] }.to_h
|
|
22
|
+
when String then object.instance_eval(getter) rescue nil
|
|
23
|
+
when Symbol then object.public_send(getter) rescue nil
|
|
24
|
+
when ->(p) { p.respond_to? :to_proc } then getter.to_proc.call(object) rescue nil
|
|
25
|
+
else raise ArgumentError.new "Expected Array, Hash, String, Symbol or Proc. Got: #{getter.class}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def omnivorous_to_h obj, levels = 1, collected = [], default = nil
|
|
30
|
+
return default ? "∃#{instance_eval(default)}" : obj if collected.include? obj
|
|
31
|
+
|
|
32
|
+
levels < 0 ? obj :
|
|
33
|
+
case obj
|
|
34
|
+
when Array
|
|
35
|
+
obj.map { |e| omnivorous_to_h e, levels, collected, default }
|
|
36
|
+
when ->(o) { o.methods.include?(:to_h) && o.method(:to_h).parameters.map(&:first) == [:opt, :opt] }
|
|
37
|
+
obj.to_h levels - 1, collected << obj
|
|
38
|
+
else
|
|
39
|
+
obj
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.included base
|
|
45
|
+
base.extend ClassMethods
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module ClassMethods
|
|
49
|
+
def store_variable name, value
|
|
50
|
+
Utils.store_variable self, name, value
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def lookup_variable name
|
|
55
|
+
Utils.lookup_variable self, name
|
|
56
|
+
end
|
|
57
|
+
def lookup_variable_value name
|
|
58
|
+
Utils.lookup_variable_value self, name
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kantox-split
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Kantox LTD
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2016-05-12 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rgl
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.5'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.5'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: kungfuig
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.7'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.7'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '10.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '10.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '2.12'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '2.12'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: cucumber
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.3'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.3'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: yard
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
description: This gem is extremely useful while in process of migration to another
|
|
112
|
+
data source.
|
|
113
|
+
email:
|
|
114
|
+
- aleksei.matiushkin@kantox.com
|
|
115
|
+
executables: []
|
|
116
|
+
extensions: []
|
|
117
|
+
extra_rdoc_files: []
|
|
118
|
+
files:
|
|
119
|
+
- ".gitignore"
|
|
120
|
+
- ".rspec"
|
|
121
|
+
- ".rubocop.yml"
|
|
122
|
+
- ".rubocop_todo.yml"
|
|
123
|
+
- ".travis.yml"
|
|
124
|
+
- Gemfile
|
|
125
|
+
- README.md
|
|
126
|
+
- Rakefile
|
|
127
|
+
- bin/console
|
|
128
|
+
- bin/setup
|
|
129
|
+
- kantox-split.gemspec
|
|
130
|
+
- lib/kantox/split.rb
|
|
131
|
+
- lib/kantox/split/adapters.rb
|
|
132
|
+
- lib/kantox/split/adapters/getters.rb
|
|
133
|
+
- lib/kantox/split/graph.rb
|
|
134
|
+
- lib/kantox/split/hooker.rb
|
|
135
|
+
- lib/kantox/split/utils.rb
|
|
136
|
+
- lib/kantox/split/version.rb
|
|
137
|
+
homepage: http://kantox.com
|
|
138
|
+
licenses: []
|
|
139
|
+
metadata: {}
|
|
140
|
+
post_install_message:
|
|
141
|
+
rdoc_options: []
|
|
142
|
+
require_paths:
|
|
143
|
+
- lib
|
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
145
|
+
requirements:
|
|
146
|
+
- - ">="
|
|
147
|
+
- !ruby/object:Gem::Version
|
|
148
|
+
version: '0'
|
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
|
+
requirements:
|
|
151
|
+
- - ">="
|
|
152
|
+
- !ruby/object:Gem::Version
|
|
153
|
+
version: '0'
|
|
154
|
+
requirements: []
|
|
155
|
+
rubyforge_project:
|
|
156
|
+
rubygems_version: 2.4.8
|
|
157
|
+
signing_key:
|
|
158
|
+
specification_version: 4
|
|
159
|
+
summary: Easy split ActiveRecord’s CUD operations to write to separate data source.
|
|
160
|
+
test_files: []
|
|
161
|
+
has_rdoc:
|