partial_ks 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +21 -0
- data/README.md +10 -3
- data/lib/partial_ks/all_rails_models.rb +9 -0
- data/lib/partial_ks/configuration_generator.rb +7 -8
- data/lib/partial_ks/filtered_table.rb +20 -2
- data/lib/partial_ks/parent_inferrer.rb +3 -3
- data/lib/partial_ks/runner.rb +3 -11
- data/lib/partial_ks/table.rb +10 -13
- data/lib/partial_ks/version.rb +1 -1
- data/lib/partial_ks.rb +1 -0
- data/partial_ks.gemspec +1 -1
- data/test/configuration_generator_test.rb +8 -25
- data/test/filtered_table_test.rb +15 -6
- data/test/parent_inferrer_test.rb +6 -6
- data/test/runner_test.rb +5 -5
- data/test/schema.rb +10 -0
- data/test/setup_models.rb +8 -0
- data/test/table_test.rb +9 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6cd50565fe47324715461ded8eb3123a717f1f8
|
4
|
+
data.tar.gz: 2c5c3a0ab04b6a056b29760bc4b4a8aa1a061a24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9e40d3d927f2b87ddb13831701eabb48d0fa8d9842fc1a47fcd7d4b87fe20f8244a16c3cff410303c5ce651f8e3e85334873f85eecf524df911a6c96d6e2989
|
7
|
+
data.tar.gz: 0108a674dc8eb3c0ab3bba60c11b8f9601d7e64e553b1f2a00e72c48594eeae04c8b30c27de663cc1ed2d0f6fab3fb0ba67090273ea476f12f51041fee012980
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
# 0.1.0
|
4
|
+
|
5
|
+
* Switch to using Rails models rather than table names.
|
6
|
+
|
7
|
+
You must now specify rails models in your manual_configuration.
|
8
|
+
|
9
|
+
Remember, if you want to configure all models, you must eager load in development.
|
10
|
+
|
11
|
+
```
|
12
|
+
Rails.application.eager_load!
|
13
|
+
Rails::Engine.subclasses.map(&:eager_load!)
|
14
|
+
```
|
15
|
+
|
16
|
+
* Reject polymorphic relationships from ever being a candidate parent.
|
17
|
+
|
18
|
+
This has the implication that a table that wasn't considered top level could be
|
19
|
+
top level.
|
20
|
+
|
21
|
+
* Bugfix: Emit valid where SQL fragments for models which has a assocation with `:foreign_key`
|
22
|
+
that does not follow convention
|
23
|
+
|
3
24
|
## 0.0.6
|
4
25
|
|
5
26
|
Allow manual_configuration to transition to using model names (moving off from table names)
|
data/README.md
CHANGED
@@ -18,15 +18,22 @@ You can specify manual configurations if needed.
|
|
18
18
|
|
19
19
|
```
|
20
20
|
manual_configuration = [
|
21
|
-
[
|
22
|
-
[
|
21
|
+
[User, nil, User.where(:id => [1])], # specify a subset of users. as users have no parent, specify `nil`
|
22
|
+
[BlogPost, User] # filter blog_posts by User
|
23
23
|
]
|
24
24
|
```
|
25
25
|
|
26
26
|
TODO :
|
27
27
|
|
28
28
|
* Rename PartialKs::ConfigurationGenerator#call to something better
|
29
|
-
* What in the world is the tuples in `manual_configuration` meant to be ?
|
30
29
|
* Minimize Public API
|
30
|
+
* Tool to run report using bundle exec
|
31
|
+
|
32
|
+
# Not supported
|
33
|
+
|
34
|
+
Things that are not supported in this version.
|
35
|
+
|
36
|
+
* Polymorphic relationships
|
37
|
+
* Tables with STI
|
31
38
|
|
32
39
|
|
@@ -3,11 +3,11 @@
|
|
3
3
|
# and attempts to automatically populate the table into the table graph
|
4
4
|
module PartialKs
|
5
5
|
class ConfigurationGenerator
|
6
|
-
attr_reader :manual_configuration, :
|
6
|
+
attr_reader :manual_configuration, :models
|
7
7
|
|
8
|
-
def initialize(manual_configuration,
|
8
|
+
def initialize(manual_configuration, models: nil)
|
9
9
|
@manual_configuration = manual_configuration
|
10
|
-
@
|
10
|
+
@models = models || PartialKs.all_rails_models
|
11
11
|
end
|
12
12
|
|
13
13
|
def call
|
@@ -16,14 +16,14 @@ module PartialKs
|
|
16
16
|
|
17
17
|
protected
|
18
18
|
def all_tables
|
19
|
-
@all_tables ||=
|
19
|
+
@all_tables ||= models.map {|model| PartialKs::Table.new(model) }.select(&:model?).index_by(&:table_name)
|
20
20
|
end
|
21
21
|
|
22
22
|
def filtered_tables
|
23
23
|
synced_tables = {}
|
24
24
|
|
25
|
-
manual_configuration.each do |
|
26
|
-
table_name =
|
25
|
+
manual_configuration.each do |model, specified_parent_model, filter_for_table|
|
26
|
+
table_name = model.table_name
|
27
27
|
next unless all_tables[table_name]
|
28
28
|
|
29
29
|
parent_model = specified_parent_model
|
@@ -35,8 +35,7 @@ module PartialKs
|
|
35
35
|
|
36
36
|
begin
|
37
37
|
inferrer = PartialKs::ParentInferrer.new(table)
|
38
|
-
|
39
|
-
parent_model = all_tables[inferrer.inferred_parent_table].try!(:model)
|
38
|
+
parent_model = inferrer.inferred_parent_class
|
40
39
|
synced_tables[table_name] = PartialKs::FilteredTable.new(table, parent_model)
|
41
40
|
rescue PartialKs::ParentInferrer::CannotInfer
|
42
41
|
next
|
@@ -8,7 +8,25 @@ class PartialKs::FilteredTable
|
|
8
8
|
@custom_filter_relation = custom_filter_relation
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
custom_filter_relation || parent_model
|
11
|
+
def kitchen_sync_filter
|
12
|
+
filter_condition = custom_filter_relation || parent_model
|
13
|
+
|
14
|
+
if !filter_condition.nil?
|
15
|
+
if filter_condition.is_a?(ActiveRecord::Relation) || filter_condition.respond_to?(:where_sql)
|
16
|
+
only_filter = filter_condition.where_sql.to_s.sub("WHERE", "")
|
17
|
+
elsif filter_condition.is_a?(String)
|
18
|
+
only_filter = filter_condition
|
19
|
+
else
|
20
|
+
# this only supports parents where it's a belongs_to
|
21
|
+
# TODO we can make it work with has_many
|
22
|
+
# e.g. SomeModel.reflect_on_association(:elses)
|
23
|
+
association = table.model.reflect_on_all_associations(:belongs_to).find {|assoc| assoc.class_name == filter_condition.name}
|
24
|
+
raise "#{filter_condition.name} not found in #{table.model.name} associations" if association.nil?
|
25
|
+
|
26
|
+
only_filter = "#{association.foreign_key} IN (#{[0, *filter_condition.pluck(:id)].join(',')})"
|
27
|
+
end
|
28
|
+
|
29
|
+
{"only" => only_filter}
|
30
|
+
end
|
13
31
|
end
|
14
32
|
end
|
@@ -7,11 +7,11 @@ class PartialKs::ParentInferrer
|
|
7
7
|
@table = table
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def inferred_parent_class
|
11
11
|
if table.top_level_table?
|
12
12
|
nil
|
13
|
-
elsif table.
|
14
|
-
table.
|
13
|
+
elsif table.candidate_parent_classes.size == 1
|
14
|
+
table.candidate_parent_classes.first
|
15
15
|
else
|
16
16
|
raise CannotInfer, "table has multiple candidates for parents"
|
17
17
|
end
|
data/lib/partial_ks/runner.rb
CHANGED
@@ -12,18 +12,10 @@ class PartialKs::Runner
|
|
12
12
|
|
13
13
|
generation.each do |table|
|
14
14
|
table_names << table.table_name
|
15
|
-
filter_config = table.
|
15
|
+
filter_config = table.kitchen_sync_filter
|
16
16
|
|
17
17
|
if !filter_config.nil?
|
18
|
-
|
19
|
-
only_filter = filter_config.where_sql.to_s.sub("WHERE", "")
|
20
|
-
elsif filter_config.is_a?(String)
|
21
|
-
only_filter = filter_config
|
22
|
-
else
|
23
|
-
only_filter = "#{filter_config.to_s.foreign_key} IN (#{[0, *filter_config.pluck(:id)].join(',')})"
|
24
|
-
end
|
25
|
-
|
26
|
-
tables_to_filter[table.table_name] = {"only" => only_filter}
|
18
|
+
tables_to_filter[table.table_name] = filter_config
|
27
19
|
end
|
28
20
|
end
|
29
21
|
|
@@ -36,7 +28,7 @@ class PartialKs::Runner
|
|
36
28
|
result = []
|
37
29
|
each_generation.with_index do |generation, depth|
|
38
30
|
generation.each do |table|
|
39
|
-
result << [table.table_name, table.parent_model, table.
|
31
|
+
result << [table.table_name, table.parent_model, table.parent_model || table.custom_filter_relation, depth]
|
40
32
|
end
|
41
33
|
end
|
42
34
|
result
|
data/lib/partial_ks/table.rb
CHANGED
@@ -1,15 +1,10 @@
|
|
1
1
|
module PartialKs
|
2
2
|
class Table
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :model
|
4
|
+
delegate :table_name, :to => :model
|
4
5
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
end
|
8
|
-
|
9
|
-
def model
|
10
|
-
@model ||= table_name.classify.constantize
|
11
|
-
rescue NameError
|
12
|
-
nil
|
6
|
+
def initialize(model)
|
7
|
+
@model = model
|
13
8
|
end
|
14
9
|
|
15
10
|
# sometimes the table is present, but the model is not defined
|
@@ -19,15 +14,17 @@ module PartialKs
|
|
19
14
|
end
|
20
15
|
|
21
16
|
def top_level_table?
|
22
|
-
|
17
|
+
candidate_parent_classes.empty?
|
23
18
|
end
|
24
19
|
|
25
|
-
|
26
|
-
|
20
|
+
# NB: can't do polymorphic for now, rails errors on reflection#klass
|
21
|
+
# see, e.g. https://github.com/rails/rails/issues/15833
|
22
|
+
def candidate_parent_classes
|
23
|
+
non_nullable_reflections.reject(&:polymorphic?).map(&:klass)
|
27
24
|
end
|
28
25
|
|
29
26
|
def parent_tables
|
30
|
-
belongs_to_reflections.map(&:
|
27
|
+
belongs_to_reflections.map(&:table_name)
|
31
28
|
end
|
32
29
|
|
33
30
|
private
|
data/lib/partial_ks/version.rb
CHANGED
data/lib/partial_ks.rb
CHANGED
data/partial_ks.gemspec
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
def pks_tables(*args)
|
4
|
-
args
|
5
|
-
end
|
6
|
-
|
7
3
|
def generator(manual, tables)
|
8
4
|
PartialKs::ConfigurationGenerator.new(manual, tables).call.
|
9
5
|
map {|f| [f.table_name, f.parent_model, f.custom_filter_relation] }
|
@@ -12,12 +8,12 @@ end
|
|
12
8
|
describe "generating dependencies" do
|
13
9
|
let(:manual_configuration) do
|
14
10
|
[
|
15
|
-
[
|
11
|
+
[User, nil, User.where(:id => [1])],
|
16
12
|
]
|
17
13
|
end
|
18
14
|
|
19
15
|
it "auto infers single belongs-to dependencies" do
|
20
|
-
generator(manual_configuration,
|
16
|
+
generator(manual_configuration, models: [User, BlogPost]).
|
21
17
|
must_equal [
|
22
18
|
["users", nil, User.where(:id => [1])],
|
23
19
|
["blog_posts", User, nil]
|
@@ -25,34 +21,21 @@ describe "generating dependencies" do
|
|
25
21
|
end
|
26
22
|
|
27
23
|
it "auto infers top level tables" do
|
28
|
-
generator(manual_configuration,
|
24
|
+
generator(manual_configuration, models: [User, Tag]).
|
29
25
|
must_equal [
|
30
26
|
["users", nil, User.where(:id => [1])],
|
31
27
|
["tags", nil, nil]
|
32
28
|
]
|
33
29
|
end
|
34
|
-
end
|
35
30
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
[User, nil, User.where(:id => [1])],
|
40
|
-
]
|
41
|
-
end
|
31
|
+
it "can infer models with different table_name" do
|
32
|
+
model = OldEntry
|
33
|
+
model.table_name.wont_equal model.name.tableize
|
42
34
|
|
43
|
-
|
44
|
-
generator(manual_configuration, table_names: pks_tables("users", "blog_posts")).
|
35
|
+
generator(manual_configuration, models: [User, model]).
|
45
36
|
must_equal [
|
46
37
|
["users", nil, User.where(:id => [1])],
|
47
|
-
["
|
48
|
-
]
|
49
|
-
end
|
50
|
-
|
51
|
-
it "auto infers top level tables" do
|
52
|
-
generator(manual_configuration, table_names: pks_tables("users", "tags")).
|
53
|
-
must_equal [
|
54
|
-
["users", nil, User.where(:id => [1])],
|
55
|
-
["tags", nil, nil]
|
38
|
+
["cms_table", nil, nil]
|
56
39
|
]
|
57
40
|
end
|
58
41
|
end
|
data/test/filtered_table_test.rb
CHANGED
@@ -1,22 +1,31 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
describe "filter
|
4
|
-
let(:table) { PartialKs::Table.new(
|
3
|
+
describe "kitchen sync filter" do
|
4
|
+
let(:table) { PartialKs::Table.new(PostTag) }
|
5
5
|
|
6
6
|
it "uses parent as the filter" do
|
7
7
|
parent = Tag
|
8
8
|
filtered_table = PartialKs::FilteredTable.new(table, parent)
|
9
|
-
filtered_table.
|
9
|
+
filtered_table.kitchen_sync_filter.must_equal({"only" => 'tag_id IN (0)'})
|
10
10
|
end
|
11
11
|
|
12
12
|
it "uses the custom filter if provided" do
|
13
|
-
filter =
|
13
|
+
filter = PostTag.where(:id => [1, 2])
|
14
14
|
filtered_table = PartialKs::FilteredTable.new(table, nil, custom_filter_relation: filter)
|
15
|
-
filtered_table.
|
15
|
+
filtered_table.kitchen_sync_filter.must_equal({"only" => ' "post_tags"."id" IN (1, 2)'})
|
16
16
|
end
|
17
17
|
|
18
18
|
it "returns nil if parent is nil" do
|
19
19
|
filtered_table = PartialKs::FilteredTable.new(table, nil)
|
20
|
-
filtered_table.
|
20
|
+
filtered_table.kitchen_sync_filter.must_be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "table with different :foreign_key" do
|
24
|
+
let(:table) { PartialKs::Table.new(OldTag) }
|
25
|
+
|
26
|
+
it "uses the foreign key that's present in the table" do
|
27
|
+
filtered_table = PartialKs::FilteredTable.new(table, OldEntry)
|
28
|
+
filtered_table.kitchen_sync_filter.must_equal({"only" => 'blog_post_id IN (0)'})
|
29
|
+
end
|
21
30
|
end
|
22
31
|
end
|
@@ -2,17 +2,17 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
describe "inferring parents" do
|
4
4
|
it "infers no parent for a top level table" do
|
5
|
-
table = PartialKs::Table.new(
|
6
|
-
PartialKs::ParentInferrer.new(table).
|
5
|
+
table = PartialKs::Table.new(Tag)
|
6
|
+
PartialKs::ParentInferrer.new(table).inferred_parent_class.must_be_nil
|
7
7
|
end
|
8
8
|
|
9
9
|
it "infers a parent for a table that has a single belongs_to" do
|
10
|
-
table = PartialKs::Table.new(
|
11
|
-
PartialKs::ParentInferrer.new(table).
|
10
|
+
table = PartialKs::Table.new(BlogPost)
|
11
|
+
PartialKs::ParentInferrer.new(table).inferred_parent_class.must_equal User
|
12
12
|
end
|
13
13
|
|
14
14
|
it "infers no parent for a table has multiple belongs_to" do
|
15
|
-
table = PartialKs::Table.new(
|
16
|
-
lambda { PartialKs::ParentInferrer.new(table).
|
15
|
+
table = PartialKs::Table.new(PostTag)
|
16
|
+
lambda { PartialKs::ParentInferrer.new(table).inferred_parent_class }.must_raise PartialKs::ParentInferrer::CannotInfer
|
17
17
|
end
|
18
18
|
end
|
data/test/runner_test.rb
CHANGED
@@ -18,9 +18,9 @@ describe 'running based on output from generator' do
|
|
18
18
|
|
19
19
|
let(:generator_output) do
|
20
20
|
[
|
21
|
-
PartialKs::FilteredTable.new(PartialKs::Table.new(
|
22
|
-
PartialKs::FilteredTable.new(PartialKs::Table.new(
|
23
|
-
PartialKs::FilteredTable.new(PartialKs::Table.new(
|
21
|
+
PartialKs::FilteredTable.new(PartialKs::Table.new(User), nil, custom_filter_relation: User.where(:id => [1])),
|
22
|
+
PartialKs::FilteredTable.new(PartialKs::Table.new(Tag), nil),
|
23
|
+
PartialKs::FilteredTable.new(PartialKs::Table.new(BlogPost), User),
|
24
24
|
]
|
25
25
|
end
|
26
26
|
|
@@ -45,7 +45,7 @@ describe 'running based on output from generator' do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it "yields all non-null filters" do
|
48
|
-
expected_filters = generator_output.each_with_object({}) {|ft, hash| hash[ft.table_name] = ft.
|
48
|
+
expected_filters = generator_output.each_with_object({}) {|ft, hash| hash[ft.table_name] = ft.kitchen_sync_filter if ft.kitchen_sync_filter}
|
49
49
|
actual_filters = {}
|
50
50
|
|
51
51
|
runner.run! do |tables_to_filter, table_names|
|
@@ -55,7 +55,7 @@ describe 'running based on output from generator' do
|
|
55
55
|
actual_filters.size.must_equal expected_filters.size
|
56
56
|
actual_filters.keys.must_equal expected_filters.keys
|
57
57
|
expected_filters.each do |table_name, filter_condition|
|
58
|
-
actual_filters[table_name]["only"].must_be_kind_of String
|
58
|
+
actual_filters[table_name]["only"].must_be_kind_of String
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
data/test/schema.rb
CHANGED
@@ -20,4 +20,14 @@ ActiveRecord::Schema.define(:version => 0) do
|
|
20
20
|
t.references :tag, null: false
|
21
21
|
t.timestamps null: false
|
22
22
|
end
|
23
|
+
|
24
|
+
create_table :cms_table, :force => true do |t|
|
25
|
+
t.string :cms_id
|
26
|
+
t.string :some_legacy_thing
|
27
|
+
end
|
28
|
+
|
29
|
+
create_table :old_tags, :force => true do |t|
|
30
|
+
t.integer :blog_post_id
|
31
|
+
t.timestamps null: false
|
32
|
+
end
|
23
33
|
end
|
data/test/setup_models.rb
CHANGED
@@ -12,3 +12,11 @@ class PostTag < ActiveRecord::Base
|
|
12
12
|
belongs_to :blog_post
|
13
13
|
belongs_to :tag
|
14
14
|
end
|
15
|
+
|
16
|
+
class OldEntry < ActiveRecord::Base
|
17
|
+
self.table_name = "cms_table"
|
18
|
+
end
|
19
|
+
|
20
|
+
class OldTag < ActiveRecord::Base
|
21
|
+
belongs_to :old_entry, :foreign_key => 'blog_post_id'
|
22
|
+
end
|
data/test/table_test.rb
CHANGED
@@ -3,8 +3,16 @@ require 'test_helper'
|
|
3
3
|
describe PartialKs::Table do
|
4
4
|
describe "#model" do
|
5
5
|
it "has a model" do
|
6
|
-
table = PartialKs::Table.new(
|
6
|
+
table = PartialKs::Table.new(User)
|
7
7
|
table.must_respond_to :model
|
8
8
|
end
|
9
9
|
end
|
10
|
+
|
11
|
+
describe "candidate parents" do
|
12
|
+
it "returns nothing for a top level table" do
|
13
|
+
table = PartialKs::Table.new(User)
|
14
|
+
|
15
|
+
table.candidate_parent_classes.must_equal []
|
16
|
+
end
|
17
|
+
end
|
10
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: partial_ks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thong Kuah
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- README.md
|
38
38
|
- Rakefile
|
39
39
|
- lib/partial_ks.rb
|
40
|
+
- lib/partial_ks/all_rails_models.rb
|
40
41
|
- lib/partial_ks/configuration_generator.rb
|
41
42
|
- lib/partial_ks/filtered_table.rb
|
42
43
|
- lib/partial_ks/parent_inferrer.rb
|