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 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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
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
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kantox-split.gemspec
4
+ gemspec
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,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -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
@@ -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,2 @@
1
+ require 'kantox/split/adapters/getters'
2
+ require 'kantox/split/adapters/rethink-db'
@@ -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
@@ -0,0 +1,5 @@
1
+ module Kantox
2
+ module Split
3
+ VERSION = "0.2.0"
4
+ end
5
+ 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: