partial_ks 0.0.6 → 0.1.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 +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
|