partial_ks 0.0.4 → 0.0.5
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/.gitignore +1 -0
- data/CHANGES.md +6 -0
- data/README.md +4 -4
- data/Rakefile +10 -0
- data/lib/partial_ks.rb +4 -0
- data/lib/partial_ks/configuration_generator.rb +18 -20
- data/lib/partial_ks/filtered_table.rb +14 -0
- data/lib/partial_ks/parent_inferrer.rb +19 -0
- data/lib/partial_ks/runner.rb +15 -15
- data/lib/partial_ks/version.rb +1 -1
- data/test/configuration_generator_test.rb +34 -0
- data/test/database.yml +3 -0
- data/test/filtered_table_test.rb +22 -0
- data/test/parent_inferrer_test.rb +18 -0
- data/test/runner_test.rb +61 -0
- data/test/schema.rb +23 -0
- data/test/setup_models.rb +14 -0
- data/test/table_test.rb +10 -0
- data/test/test_helper.rb +7 -0
- metadata +26 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f64ccee32ef2fecf6953f2916c03e7d13fc061a
|
4
|
+
data.tar.gz: 2666bf980392217edf9567bee84ebeee87601768
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ac534888ca683de0f4e18d6fd97d9ff245440410dc405a79a126ad4c0ff291f283dfd71128b28b95ec6cc1e9e3cad66fde6ce81d07273731b999f6ad8d8ffe1
|
7
|
+
data.tar.gz: f10c7800637417c5a7f8c4a6cc76cdf53d2be2ad9536b208e22a96f6cd1e677fce2fd060123b6844ac021d38fa3389e90621178b9224ff0fac92ddc0f0f9fd48
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
test/test.db
|
data/CHANGES.md
ADDED
data/README.md
CHANGED
@@ -10,7 +10,7 @@ So how does it work ?
|
|
10
10
|
|
11
11
|
```
|
12
12
|
manual_configuration = []
|
13
|
-
config = PartialKs::ConfigurationGenerator.new().call
|
13
|
+
config = PartialKs::ConfigurationGenerator.new(manual_configuration).call
|
14
14
|
PartialKs::Runner.new([config]).run!
|
15
15
|
```
|
16
16
|
|
@@ -25,8 +25,8 @@ manual_configuration = [
|
|
25
25
|
|
26
26
|
TODO :
|
27
27
|
|
28
|
-
Rename PartialKs::ConfigurationGenerator#call to something better
|
29
|
-
What in the world is the tuples in `manual_configuration` meant to be ?
|
30
|
-
Minimize Public API
|
28
|
+
* Rename PartialKs::ConfigurationGenerator#call to something better
|
29
|
+
* What in the world is the tuples in `manual_configuration` meant to be ?
|
30
|
+
* Minimize Public API
|
31
31
|
|
32
32
|
|
data/Rakefile
ADDED
data/lib/partial_ks.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
1
3
|
module PartialKs
|
2
4
|
end
|
3
5
|
|
6
|
+
require_relative 'partial_ks/filtered_table'
|
7
|
+
require_relative 'partial_ks/parent_inferrer'
|
4
8
|
require_relative 'partial_ks/runner'
|
5
9
|
require_relative 'partial_ks/table'
|
6
10
|
require_relative 'partial_ks/configuration_generator'
|
@@ -2,49 +2,47 @@
|
|
2
2
|
# goes through each table not already in the table graph,
|
3
3
|
# and attempts to automatically populate the table into the table graph
|
4
4
|
class PartialKs::ConfigurationGenerator
|
5
|
-
attr_reader :table_graph, :
|
5
|
+
attr_reader :table_graph, :table_names
|
6
6
|
|
7
|
-
def initialize(table_graph,
|
7
|
+
def initialize(table_graph, table_names: nil)
|
8
8
|
@table_graph = table_graph
|
9
|
-
@
|
9
|
+
@table_names = table_names || ActiveRecord::Base.connection.tables
|
10
10
|
end
|
11
11
|
|
12
12
|
def call
|
13
|
-
@
|
13
|
+
@filtered_tables ||= filtered_tables
|
14
14
|
end
|
15
15
|
|
16
16
|
protected
|
17
17
|
def all_tables
|
18
|
-
@all_tables ||=
|
18
|
+
@all_tables ||= @table_names.map {|table_name| PartialKs::Table.new(table_name) }.select(&:model?).index_by(&:table_name)
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def filtered_tables
|
22
22
|
synced_tables = {}
|
23
23
|
|
24
|
-
table_graph.each do |table_name,
|
24
|
+
table_graph.each do |table_name, specified_parent_model, filter_for_table|
|
25
25
|
next unless all_tables[table_name]
|
26
26
|
|
27
|
-
|
27
|
+
parent_model = specified_parent_model
|
28
|
+
synced_tables[table_name] = PartialKs::FilteredTable.new(all_tables[table_name], parent_model, custom_filter_relation: filter_for_table)
|
28
29
|
end
|
29
30
|
|
30
31
|
all_tables.each do |table_name, table|
|
31
32
|
next if synced_tables[table_name]
|
32
|
-
next unless table.top_level_table?
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
parent_table = all_tables[parent_table_name] if parent_table_name
|
43
|
-
next unless parent_table
|
34
|
+
begin
|
35
|
+
inferrer = PartialKs::ParentInferrer.new(table)
|
36
|
+
#TODO get rid of try!
|
37
|
+
parent_model = all_tables[inferrer.inferred_parent_table].try!(:model)
|
38
|
+
synced_tables[table_name] = PartialKs::FilteredTable.new(table, parent_model)
|
39
|
+
rescue PartialKs::ParentInferrer::CannotInfer
|
40
|
+
next
|
41
|
+
end
|
44
42
|
|
45
|
-
synced_tables[table.table_name] = [table_name, parent_table.model]
|
46
43
|
end
|
47
44
|
|
45
|
+
# TODO remove this side effect. Maybe yield or a different method call ?
|
48
46
|
puts "***************"
|
49
47
|
remaining_size = 0
|
50
48
|
all_tables.each do |table_name, table|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class PartialKs::FilteredTable
|
2
|
+
attr_reader :table, :parent_model, :custom_filter_relation
|
3
|
+
delegate :table_name, :to => :table
|
4
|
+
|
5
|
+
def initialize(table, parent_model, custom_filter_relation: nil)
|
6
|
+
@table = table
|
7
|
+
@parent_model = parent_model
|
8
|
+
@custom_filter_relation = custom_filter_relation
|
9
|
+
end
|
10
|
+
|
11
|
+
def filter_condition
|
12
|
+
custom_filter_relation || parent_model
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class PartialKs::ParentInferrer
|
2
|
+
CannotInfer = Class.new(StandardError)
|
3
|
+
|
4
|
+
attr_reader :table
|
5
|
+
|
6
|
+
def initialize(table)
|
7
|
+
@table = table
|
8
|
+
end
|
9
|
+
|
10
|
+
def inferred_parent_table
|
11
|
+
if table.top_level_table?
|
12
|
+
nil
|
13
|
+
elsif table.non_nullable_parent_tables.size == 1
|
14
|
+
table.non_nullable_parent_tables.first
|
15
|
+
else
|
16
|
+
raise CannotInfer, "table has multiple candidates for parents"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/partial_ks/runner.rb
CHANGED
@@ -10,8 +10,9 @@ class PartialKs::Runner
|
|
10
10
|
tables_to_filter = {}
|
11
11
|
table_names = []
|
12
12
|
|
13
|
-
generation.each do |
|
14
|
-
table_names << table_name
|
13
|
+
generation.each do |table|
|
14
|
+
table_names << table.table_name
|
15
|
+
filter_config = table.filter_condition
|
15
16
|
|
16
17
|
if !filter_config.nil?
|
17
18
|
if filter_config.is_a?(ActiveRecord::Relation) || filter_config.respond_to?(:where_sql)
|
@@ -22,26 +23,25 @@ class PartialKs::Runner
|
|
22
23
|
only_filter = "#{filter_config.to_s.foreign_key} IN (#{[0, *filter_config.pluck(:id)].join(',')})"
|
23
24
|
end
|
24
25
|
|
25
|
-
tables_to_filter[table_name] = {"only" => only_filter}
|
26
|
+
tables_to_filter[table.table_name] = {"only" => only_filter}
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
30
|
+
# TODO output only tables_to_filter, depending on how KS handles filters
|
29
31
|
yield tables_to_filter, table_names
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
35
|
def report
|
36
|
+
result = []
|
34
37
|
each_generation.with_index do |generation, depth|
|
35
|
-
generation.each do |
|
36
|
-
|
37
|
-
puts [table_name, table_graph.try!(:to_s), depth].inspect
|
38
|
+
generation.each do |table|
|
39
|
+
result << [table.table_name, table.parent_model, table.filter_condition, depth]
|
38
40
|
end
|
39
41
|
end
|
42
|
+
result
|
40
43
|
end
|
41
44
|
|
42
|
-
# Yields the following
|
43
|
-
# 1. table name
|
44
|
-
# 2. the foreign key columns used to filter this table, and a lambda that will return the foreign key ids
|
45
45
|
def each_generation
|
46
46
|
return enum_for(:each_generation) unless block_given?
|
47
47
|
|
@@ -52,23 +52,23 @@ class PartialKs::Runner
|
|
52
52
|
|
53
53
|
protected
|
54
54
|
def generations
|
55
|
-
return @generations if @generations
|
55
|
+
return @generations if defined?(@generations)
|
56
56
|
|
57
57
|
@generations = []
|
58
58
|
table_graphs.each do |table_graph|
|
59
59
|
q = []
|
60
60
|
|
61
|
-
table_graph.each do |
|
62
|
-
q <<
|
61
|
+
table_graph.each do |table|
|
62
|
+
q << table if table.parent_model.nil?
|
63
63
|
end
|
64
64
|
|
65
65
|
until q.empty?
|
66
66
|
@generations << q
|
67
67
|
|
68
68
|
next_generation = []
|
69
|
-
q.each do |
|
70
|
-
table_graph.each do |
|
71
|
-
next_generation <<
|
69
|
+
q.each do |table|
|
70
|
+
table_graph.each do |child_table|
|
71
|
+
next_generation << child_table if child_table.parent_model && child_table.parent_model.table_name == table.table_name
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
data/lib/partial_ks/version.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
def pks_tables(*args)
|
4
|
+
args
|
5
|
+
end
|
6
|
+
|
7
|
+
def generator(manual, tables)
|
8
|
+
PartialKs::ConfigurationGenerator.new(manual, tables).call.
|
9
|
+
map {|f| [f.table_name, f.parent_model, f.custom_filter_relation] }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "generating dependencies" do
|
13
|
+
let(:manual_configuration) do
|
14
|
+
[
|
15
|
+
["users", nil, User.where(:id => [1])],
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "auto infers single belongs-to dependencies" do
|
20
|
+
generator(manual_configuration, table_names: pks_tables("users", "blog_posts")).
|
21
|
+
must_equal [
|
22
|
+
["users", nil, User.where(:id => [1])],
|
23
|
+
["blog_posts", User, nil]
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "auto infers top level tables" do
|
28
|
+
generator(manual_configuration, table_names: pks_tables("users", "tags")).
|
29
|
+
must_equal [
|
30
|
+
["users", nil, User.where(:id => [1])],
|
31
|
+
["tags", nil, nil]
|
32
|
+
]
|
33
|
+
end
|
34
|
+
end
|
data/test/database.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe "filter condition" do
|
4
|
+
let(:table) { PartialKs::Table.new("users") }
|
5
|
+
|
6
|
+
it "uses parent as the filter" do
|
7
|
+
parent = Tag
|
8
|
+
filtered_table = PartialKs::FilteredTable.new(table, parent)
|
9
|
+
filtered_table.filter_condition.must_equal parent
|
10
|
+
end
|
11
|
+
|
12
|
+
it "uses the custom filter if provided" do
|
13
|
+
filter = User.where(:id => 1)
|
14
|
+
filtered_table = PartialKs::FilteredTable.new(table, nil, custom_filter_relation: filter)
|
15
|
+
filtered_table.filter_condition.must_equal filter
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns nil if parent is nil" do
|
19
|
+
filtered_table = PartialKs::FilteredTable.new(table, nil)
|
20
|
+
filtered_table.filter_condition.must_be_nil
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe "inferring parents" do
|
4
|
+
it "infers no parent for a top level table" do
|
5
|
+
table = PartialKs::Table.new("tags")
|
6
|
+
PartialKs::ParentInferrer.new(table).inferred_parent_table.must_equal nil
|
7
|
+
end
|
8
|
+
|
9
|
+
it "infers a parent for a table that has a single belongs_to" do
|
10
|
+
table = PartialKs::Table.new("blog_posts")
|
11
|
+
PartialKs::ParentInferrer.new(table).inferred_parent_table.must_equal "users"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "infers no parent for a table has multiple belongs_to" do
|
15
|
+
table = PartialKs::Table.new("post_tags")
|
16
|
+
lambda { PartialKs::ParentInferrer.new(table).inferred_parent_table }.must_raise PartialKs::ParentInferrer::CannotInfer
|
17
|
+
end
|
18
|
+
end
|
data/test/runner_test.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe 'end to end testing' do
|
4
|
+
it "runs without error" do
|
5
|
+
generator_output = PartialKs::ConfigurationGenerator.new([]).call
|
6
|
+
PartialKs::Runner.new([generator_output]).run! do |tables_to_filter, table_names|
|
7
|
+
# left empty
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'running based on output from generator' do
|
13
|
+
let(:manual_configuration) do
|
14
|
+
[
|
15
|
+
["users", nil, User.where(:id => [1])],
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:generator_output) do
|
20
|
+
[
|
21
|
+
PartialKs::FilteredTable.new(PartialKs::Table.new("users"), nil, custom_filter_relation: User.where(:id => [1])),
|
22
|
+
PartialKs::FilteredTable.new(PartialKs::Table.new("tags"), nil),
|
23
|
+
PartialKs::FilteredTable.new(PartialKs::Table.new("blog_posts"), User),
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:runner) { PartialKs::Runner.new([generator_output]) }
|
28
|
+
|
29
|
+
it "reports everything" do
|
30
|
+
runner.report.must_equal [
|
31
|
+
["users", nil, User.where(:id => [1]), 0],
|
32
|
+
["tags", nil, nil, 0],
|
33
|
+
["blog_posts", User, User, 1],
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "yields all table names" do
|
38
|
+
expected_table_names = generator_output.map(&:table_name)
|
39
|
+
actual_table_names = []
|
40
|
+
runner.run! do |tables_to_filter, table_names|
|
41
|
+
actual_table_names += table_names
|
42
|
+
end
|
43
|
+
|
44
|
+
actual_table_names.must_equal expected_table_names
|
45
|
+
end
|
46
|
+
|
47
|
+
it "yields all non-null filters" do
|
48
|
+
expected_filters = generator_output.each_with_object({}) {|ft, hash| hash[ft.table_name] = ft.filter_condition if ft.filter_condition}
|
49
|
+
actual_filters = {}
|
50
|
+
|
51
|
+
runner.run! do |tables_to_filter, table_names|
|
52
|
+
actual_filters.merge!(tables_to_filter)
|
53
|
+
end
|
54
|
+
|
55
|
+
actual_filters.size.must_equal expected_filters.size
|
56
|
+
actual_filters.keys.must_equal expected_filters.keys
|
57
|
+
expected_filters.each do |table_name, filter_condition|
|
58
|
+
actual_filters[table_name]["only"].must_be_kind_of String # TODO test different kinds of filters.
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/test/schema.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :users, :force => true do |t|
|
3
|
+
t.string :email
|
4
|
+
t.timestamps null: false
|
5
|
+
end
|
6
|
+
|
7
|
+
create_table :blog_posts, :force => true do |t|
|
8
|
+
t.string :title
|
9
|
+
t.references :user, null: false
|
10
|
+
t.timestamps null: false
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :tags, :force => true do |t|
|
14
|
+
t.string :name
|
15
|
+
t.timestamps null: false
|
16
|
+
end
|
17
|
+
|
18
|
+
create_table :post_tags, :force => true do |t|
|
19
|
+
t.references :blog_post, null: false
|
20
|
+
t.references :tag, null: false
|
21
|
+
t.timestamps null: false
|
22
|
+
end
|
23
|
+
end
|
data/test/table_test.rb
ADDED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'partial_ks'
|
3
|
+
|
4
|
+
require 'setup_models'
|
5
|
+
ActiveRecord::Base.configurations = YAML::load(IO.read(File.join(File.dirname(__FILE__), "database.yml")))
|
6
|
+
ActiveRecord::Base.establish_connection ActiveRecord::Base.configurations['test']
|
7
|
+
load(File.join(File.dirname(__FILE__), "/schema.rb"))
|
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.0.5
|
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-11-
|
11
|
+
date: 2016-11-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -31,14 +31,28 @@ executables: []
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- CHANGES.md
|
34
36
|
- LICENSE.txt
|
35
37
|
- README.md
|
38
|
+
- Rakefile
|
36
39
|
- lib/partial_ks.rb
|
37
40
|
- lib/partial_ks/configuration_generator.rb
|
41
|
+
- lib/partial_ks/filtered_table.rb
|
42
|
+
- lib/partial_ks/parent_inferrer.rb
|
38
43
|
- lib/partial_ks/runner.rb
|
39
44
|
- lib/partial_ks/table.rb
|
40
45
|
- lib/partial_ks/version.rb
|
41
46
|
- partial_ks.gemspec
|
47
|
+
- test/configuration_generator_test.rb
|
48
|
+
- test/database.yml
|
49
|
+
- test/filtered_table_test.rb
|
50
|
+
- test/parent_inferrer_test.rb
|
51
|
+
- test/runner_test.rb
|
52
|
+
- test/schema.rb
|
53
|
+
- test/setup_models.rb
|
54
|
+
- test/table_test.rb
|
55
|
+
- test/test_helper.rb
|
42
56
|
homepage: https://github.com/powershop/partial_ks
|
43
57
|
licenses:
|
44
58
|
- MIT
|
@@ -63,4 +77,13 @@ rubygems_version: 2.2.5
|
|
63
77
|
signing_key:
|
64
78
|
specification_version: 4
|
65
79
|
summary: Partial KS
|
66
|
-
test_files:
|
80
|
+
test_files:
|
81
|
+
- test/configuration_generator_test.rb
|
82
|
+
- test/database.yml
|
83
|
+
- test/filtered_table_test.rb
|
84
|
+
- test/parent_inferrer_test.rb
|
85
|
+
- test/runner_test.rb
|
86
|
+
- test/schema.rb
|
87
|
+
- test/setup_models.rb
|
88
|
+
- test/table_test.rb
|
89
|
+
- test/test_helper.rb
|