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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 68505ca861cdb0855c62be32cd6b9377f5c6f687
4
- data.tar.gz: 1dabcb9334a38d27ccbf823237a031aadc4c8b26
3
+ metadata.gz: a6cd50565fe47324715461ded8eb3123a717f1f8
4
+ data.tar.gz: 2c5c3a0ab04b6a056b29760bc4b4a8aa1a061a24
5
5
  SHA512:
6
- metadata.gz: fcb48cf2bdcc7151bb99b69a684798185ff6344cc7c7f77fd4316642d0b8ff0eb93f3e00a1f3d41c47f02334473f3b11d9c1a842a32abe976e5cd46e0a6b54e7
7
- data.tar.gz: 9e50c50f69aab5dff5a040943406d90e35241baa3adf0cef32ea8ce5abbb685a65839d236c9566cddcce5ab86408cb6227402c9bd04c4aa1c8c947c73e39fdc6
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
- ["users", nil, User.where(:id => [1])], # specify a subset of users. as users have no parent, specify `nil`
22
- ["blog_posts", User] # filter blog_posts by User
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
 
@@ -0,0 +1,9 @@
1
+ module PartialKs
2
+ def self.all_rails_models
3
+ if defined?(Rails)
4
+ ::Rails.application.eager_load!
5
+ ::Rails::Engine.subclasses.map(&:eager_load!)
6
+ end
7
+ ActiveRecord::Base.direct_descendants
8
+ end
9
+ end
@@ -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, :table_names
6
+ attr_reader :manual_configuration, :models
7
7
 
8
- def initialize(manual_configuration, table_names: nil)
8
+ def initialize(manual_configuration, models: nil)
9
9
  @manual_configuration = manual_configuration
10
- @table_names = table_names || ActiveRecord::Base.connection.tables
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 ||= @table_names.map {|table_name| PartialKs::Table.new(table_name) }.select(&:model?).index_by(&:table_name)
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 |table_name_or_model, specified_parent_model, filter_for_table|
26
- table_name = table_name_or_model.is_a?(String) ? table_name_or_model : table_name_or_model.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
- #TODO get rid of try!
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 filter_condition
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 inferred_parent_table
10
+ def inferred_parent_class
11
11
  if table.top_level_table?
12
12
  nil
13
- elsif table.non_nullable_parent_tables.size == 1
14
- table.non_nullable_parent_tables.first
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
@@ -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.filter_condition
15
+ filter_config = table.kitchen_sync_filter
16
16
 
17
17
  if !filter_config.nil?
18
- if filter_config.is_a?(ActiveRecord::Relation) || filter_config.respond_to?(:where_sql)
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.filter_condition, depth]
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
@@ -1,15 +1,10 @@
1
1
  module PartialKs
2
2
  class Table
3
- attr_reader :table_name
3
+ attr_reader :model
4
+ delegate :table_name, :to => :model
4
5
 
5
- def initialize(table_name)
6
- @table_name = table_name
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
- non_nullable_reflections.empty?
17
+ candidate_parent_classes.empty?
23
18
  end
24
19
 
25
- def non_nullable_parent_tables
26
- non_nullable_reflections.map(&:plural_name)
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(&:plural_name)
27
+ belongs_to_reflections.map(&:table_name)
31
28
  end
32
29
 
33
30
  private
@@ -1,3 +1,3 @@
1
1
  module PartialKs
2
- VERSION = '0.0.6'
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/partial_ks.rb CHANGED
@@ -3,6 +3,7 @@ require 'active_record'
3
3
  module PartialKs
4
4
  end
5
5
 
6
+ require_relative 'partial_ks/all_rails_models'
6
7
  require_relative 'partial_ks/filtered_table'
7
8
  require_relative 'partial_ks/parent_inferrer'
8
9
  require_relative 'partial_ks/runner'
data/partial_ks.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  require File.expand_path('../lib/partial_ks/version', __FILE__)
2
2
 
3
- spec = Gem::Specification.new do |gem|
3
+ Gem::Specification.new do |gem|
4
4
  gem.name = 'partial_ks'
5
5
  gem.version = PartialKs::VERSION
6
6
  gem.summary = "Partial KS"
@@ -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
- ["users", nil, User.where(:id => [1])],
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, table_names: pks_tables("users", "blog_posts")).
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, table_names: pks_tables("users", "tags")).
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
- describe "transition to model based dependencies" do
37
- let(:manual_configuration) do
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
- it "auto infers single belongs-to dependencies" do
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
- ["blog_posts", User, nil]
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
@@ -1,22 +1,31 @@
1
1
  require 'test_helper'
2
2
 
3
- describe "filter condition" do
4
- let(:table) { PartialKs::Table.new("users") }
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.filter_condition.must_equal parent
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 = User.where(:id => 1)
13
+ filter = PostTag.where(:id => [1, 2])
14
14
  filtered_table = PartialKs::FilteredTable.new(table, nil, custom_filter_relation: filter)
15
- filtered_table.filter_condition.must_equal filter
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.filter_condition.must_be_nil
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("tags")
6
- PartialKs::ParentInferrer.new(table).inferred_parent_table.must_equal nil
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("blog_posts")
11
- PartialKs::ParentInferrer.new(table).inferred_parent_table.must_equal "users"
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("post_tags")
16
- lambda { PartialKs::ParentInferrer.new(table).inferred_parent_table }.must_raise PartialKs::ParentInferrer::CannotInfer
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("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),
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.filter_condition if ft.filter_condition}
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 # TODO test different kinds of filters.
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("users")
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.6
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-02 00:00:00.000000000 Z
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