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 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