partial_ks 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|