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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b14cf3641f82c08ea9ea16536510c59b950dd0f6
4
- data.tar.gz: ee41b04d67e616c72787e953ca341da267b15317
3
+ metadata.gz: 9f64ccee32ef2fecf6953f2916c03e7d13fc061a
4
+ data.tar.gz: 2666bf980392217edf9567bee84ebeee87601768
5
5
  SHA512:
6
- metadata.gz: 5b20e05c16bd828f7c9d0f1797fd95b9a5281f49f251f65cb4353caf176b0ef8891ad9f4aa56225935d2a890d28c223e39c827fa080e85b6713838bf96f177f5
7
- data.tar.gz: 0b9d556d6419799d9f05649198fc3b4b40e4658ecd2d08f59ad2a6193af4d27a0d8afbc4b82424cb1e236fbe9b2564bd50cb99a65ded4f8bbd56e2de192fab2c
6
+ metadata.gz: 4ac534888ca683de0f4e18d6fd97d9ff245440410dc405a79a126ad4c0ff291f283dfd71128b28b95ec6cc1e9e3cad66fde6ce81d07273731b999f6ad8d8ffe1
7
+ data.tar.gz: f10c7800637417c5a7f8c4a6cc76cdf53d2be2ad9536b208e22a96f6cd1e677fce2fd060123b6844ac021d38fa3389e90621178b9224ff0fac92ddc0f0f9fd48
@@ -0,0 +1 @@
1
+ test/test.db
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ## 0.0.5
4
+
5
+ API change for `ConfigurationGenerator` - removes `ignored_table_names` option.
6
+ Replaced with `table_names` option.
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
 
@@ -0,0 +1,10 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.pattern = 'test/*_test.rb'
6
+ t.verbose = true
7
+ end
8
+
9
+ desc "Run tests"
10
+ task :default => :test
@@ -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, :ignored_table_names
5
+ attr_reader :table_graph, :table_names
6
6
 
7
- def initialize(table_graph, ignored_table_names: [])
7
+ def initialize(table_graph, table_names: nil)
8
8
  @table_graph = table_graph
9
- @ignored_table_names = ignored_table_names
9
+ @table_names = table_names || ActiveRecord::Base.connection.tables
10
10
  end
11
11
 
12
12
  def call
13
- @auto_constructed_table_graph ||= auto_construct_table_graph
13
+ @filtered_tables ||= filtered_tables
14
14
  end
15
15
 
16
16
  protected
17
17
  def all_tables
18
- @all_tables ||= (ActiveRecord::Base.connection.tables - ignored_table_names).map {|table_name| PartialKs::Table.new(table_name) }.select(&:model?).index_by(&:table_name)
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 auto_construct_table_graph
21
+ def filtered_tables
22
22
  synced_tables = {}
23
23
 
24
- table_graph.each do |table_name, parent_table, table_graph|
24
+ table_graph.each do |table_name, specified_parent_model, filter_for_table|
25
25
  next unless all_tables[table_name]
26
26
 
27
- synced_tables[table_name] = [table_name, parent_table, table_graph]
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
- synced_tables[table_name] = [table_name, nil]
35
- end
36
-
37
- all_tables.each do |table_name, table|
38
- next if synced_tables[table_name]
39
- next unless table.non_nullable_parent_tables.size == 1
40
-
41
- parent_table_name, _, _ = synced_tables[table.non_nullable_parent_tables.only]
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
@@ -10,8 +10,9 @@ class PartialKs::Runner
10
10
  tables_to_filter = {}
11
11
  table_names = []
12
12
 
13
- generation.each do |table_name, filter_config, depth|
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 |table_name, table_graph|
36
- print " " * depth
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 |table_name, parent_table, configuration_for_table|
62
- q << [table_name, configuration_for_table || parent_table] if parent_table.nil?
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 |table_name, _|
70
- table_graph.each do |child_table_name, parent_table, configuration_for_table|
71
- next_generation << [child_table_name, configuration_for_table || parent_table] if parent_table && parent_table.table_name == table_name
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
 
@@ -1,3 +1,3 @@
1
1
  module PartialKs
2
- VERSION = '0.0.4'
2
+ VERSION = '0.0.5'
3
3
  end
@@ -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
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: test/test.db
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,14 @@
1
+ class User < ActiveRecord::Base
2
+ end
3
+
4
+ class BlogPost < ActiveRecord::Base
5
+ belongs_to :user
6
+ end
7
+
8
+ class Tag < ActiveRecord::Base
9
+ end
10
+
11
+ class PostTag < ActiveRecord::Base
12
+ belongs_to :blog_post
13
+ belongs_to :tag
14
+ end
@@ -0,0 +1,10 @@
1
+ require 'test_helper'
2
+
3
+ describe PartialKs::Table do
4
+ describe "#model" do
5
+ it "has a model" do
6
+ table = PartialKs::Table.new("users")
7
+ table.must_respond_to :model
8
+ end
9
+ end
10
+ end
@@ -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
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-03 00:00:00.000000000 Z
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