deep_pluck 1.1.0 → 1.1.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.
data/Rakefile CHANGED
@@ -1,10 +1,10 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
7
  t.test_files = FileList['test/**/*_test.rb']
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: :test
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "deep_pluck"
3
+ require 'bundler/setup'
4
+ require 'deep_pluck'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "deep_pluck"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start
data/bin/setup CHANGED
@@ -1,8 +1,8 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install --gemfile=gemfiles/4.2.gemfile
7
-
8
- # Do any other automated setup that you need to do here
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install --gemfile=gemfiles/4.2.gemfile
7
+
8
+ # Do any other automated setup that you need to do here
@@ -1,38 +1,45 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'deep_pluck/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "deep_pluck"
8
- spec.version = DeepPluck::VERSION
9
- spec.authors = ["khiav reoy"]
10
- spec.email = ["mrtmrt15xn@yahoo.com.tw"]
11
-
12
- spec.summary = %q{Use deep_pluck as a shortcut to select one or more attributes and include associated models without loading a bunch of records.}
13
- spec.description = %q{Use deep_pluck as a shortcut to select one or more attributes and include associated models without loading a bunch of records. And DRY up your code when using #as_json.}
14
- spec.homepage = "https://github.com/khiav223577/deep_pluck"
15
- spec.license = "MIT"
16
-
17
- # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
- # delete this section to allow pushing this gem to any host.
19
- #if spec.respond_to?(:metadata)
20
- # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
- #else
22
- # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
- #end
24
-
25
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
- spec.bindir = "exe"
27
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
- spec.require_paths = ["lib"]
29
-
30
- spec.add_development_dependency "bundler", "~> 1.11"
31
- spec.add_development_dependency "rake", "~> 12.0"
32
- spec.add_development_dependency "sqlite3", "~> 1.3"
33
- spec.add_development_dependency "minitest", "~> 5.0"
34
-
35
- spec.add_dependency "activerecord", ">= 3"
36
- spec.add_dependency "pluck_all", ">= 1.2.3"
37
-
38
- end
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'deep_pluck/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'deep_pluck'
8
+ spec.version = DeepPluck::VERSION
9
+ spec.authors = ['khiav reoy']
10
+ spec.email = ['mrtmrt15xn@yahoo.com.tw']
11
+
12
+ spec.summary = 'Use deep_pluck as a shortcut to select one or more attributes and include associated models without loading a bunch of records.'
13
+ spec.description = 'Use deep_pluck as a shortcut to select one or more attributes and include associated models without loading a bunch of records. And DRY up your code when using #as_json.'
14
+ spec.homepage = 'https://github.com/khiav223577/deep_pluck'
15
+ spec.license = 'MIT'
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject{|f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{^exe/}){|f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+ spec.metadata = {
30
+ 'homepage_uri' => 'https://github.com/khiav223577/deep_pluck',
31
+ 'changelog_uri' => 'https://github.com/khiav223577/deep_pluck/blob/master/CHANGELOG.md',
32
+ 'source_code_uri' => 'https://github.com/khiav223577/deep_pluck',
33
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/deep_pluck',
34
+ 'bug_tracker_uri' => 'https://github.com/khiav223577/deep_pluck/issues',
35
+ }
36
+
37
+ spec.add_development_dependency 'bundler', '>= 1.17', '< 3.x'
38
+ spec.add_development_dependency 'rake', '~> 12.0'
39
+ spec.add_development_dependency 'sqlite3', '~> 1.3'
40
+ spec.add_development_dependency 'minitest', '~> 5.0'
41
+
42
+ spec.add_dependency 'activerecord', '>= 3'
43
+ spec.add_dependency 'pluck_all', '>= 1.2.3'
44
+ spec.add_dependency 'rails_compatibility', '>= 0.0.1'
45
+ end
@@ -1,14 +1,11 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in pluck_all.gemspec
4
-
5
- gem "activerecord", "~> 3.2"
6
- gem "pluck_all", "~> 1.2.2"
7
-
8
- group :test do
9
- gem "simplecov"
10
- gem "codeclimate-test-reporter", "~> 1.0.0"
11
- end
12
-
13
- gemspec :path => "../"
14
-
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 3.2.0'
4
+ gem 'pluck_all', '~> 1.2.2'
5
+
6
+ group :test do
7
+ gem 'simplecov'
8
+ gem 'sqlite3', '~> 1.3.6'
9
+ end
10
+
11
+ gemspec path: '../'
@@ -1,14 +1,11 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in pluck_all.gemspec
4
-
5
- gem "activerecord", "~> 4.2"
6
- gem "pluck_all", "~> 1.2.2"
7
-
8
- group :test do
9
- gem "simplecov"
10
- gem "codeclimate-test-reporter", "~> 1.0.0"
11
- end
12
-
13
- gemspec :path => "../"
14
-
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 4.2.0'
4
+ gem 'pluck_all', '~> 1.2.2'
5
+
6
+ group :test do
7
+ gem 'simplecov'
8
+ gem 'sqlite3', '~> 1.3.6'
9
+ end
10
+
11
+ gemspec path: '../'
@@ -1,14 +1,11 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in pluck_all.gemspec
4
-
5
- gem "activerecord", "~> 5.0"
6
- gem "pluck_all", "~> 1.2.2"
7
-
8
- group :test do
9
- gem "simplecov"
10
- gem "codeclimate-test-reporter", "~> 1.0.0"
11
- end
12
-
13
- gemspec :path => "../"
14
-
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 5.0.0'
4
+ gem 'pluck_all', '~> 1.2.2'
5
+
6
+ group :test do
7
+ gem 'simplecov'
8
+ gem 'sqlite3', '~> 1.3.6'
9
+ end
10
+
11
+ gemspec path: '../'
@@ -1,14 +1,11 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in pluck_all.gemspec
4
-
5
- gem "activerecord", "~> 5.1"
6
- gem "pluck_all", "~> 1.2.2"
7
-
8
- group :test do
9
- gem "simplecov"
10
- gem "codeclimate-test-reporter", "~> 1.0.0"
11
- end
12
-
13
- gemspec :path => "../"
14
-
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 5.1.0'
4
+ gem 'pluck_all', '~> 1.2.2'
5
+
6
+ group :test do
7
+ gem 'simplecov'
8
+ gem 'sqlite3', '~> 1.3.6'
9
+ end
10
+
11
+ gemspec path: '../'
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 5.2.0'
4
+ gem 'pluck_all', '~> 1.2.2'
5
+
6
+ group :test do
7
+ gem 'simplecov'
8
+ gem 'sqlite3', '~> 1.3.6'
9
+ end
10
+
11
+ gemspec path: '../'
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 6.0.0'
4
+ gem 'pluck_all', '~> 2.0.4'
5
+
6
+ group :test do
7
+ gem 'simplecov'
8
+ gem 'sqlite3', '~> 1.4.1'
9
+ end
10
+
11
+ gemspec path: '../'
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 6.1.1'
4
+ gem 'pluck_all', '~> 2.2.1'
5
+
6
+ group :test do
7
+ gem 'simplecov'
8
+ gem 'sqlite3', '~> 1.4.1'
9
+ end
10
+
11
+ gemspec path: '../'
@@ -1,24 +1,23 @@
1
- require "deep_pluck/version"
2
- require 'deep_pluck/model'
3
- require 'active_record'
4
- require 'pluck_all'
5
-
6
- class ActiveRecord::Relation
7
- def deep_pluck(*args)
8
- DeepPluck::Model.new(self).add(args).load_all
9
- end
10
- end
11
-
12
- class ActiveRecord::Base
13
- def self.deep_pluck(*args)
14
- self.where('').deep_pluck(*args)
15
- end
16
-
17
- def deep_pluck(*args)
18
- hash_args = args.select{|s| s.is_a?(Hash) }
19
- other_args = args.select{|s| !s.is_a?(Hash) }
20
- preloaded_model = DeepPluck::PreloadedModel.new(self, other_args)
21
- model = DeepPluck::Model.new(self.class.where(id: id), preloaded_model: preloaded_model)
22
- return model.add(*hash_args).load_all.first
23
- end
24
- end
1
+ require 'deep_pluck/version'
2
+ require 'deep_pluck/model'
3
+ require 'active_record'
4
+ require 'pluck_all'
5
+
6
+ class ActiveRecord::Relation
7
+ def deep_pluck(*args)
8
+ DeepPluck::Model.new(self).add(args).load_all
9
+ end
10
+ end
11
+
12
+ class ActiveRecord::Base
13
+ def self.deep_pluck(*args)
14
+ where('').deep_pluck(*args)
15
+ end
16
+
17
+ def deep_pluck(*args)
18
+ hash_args, other_args = args.partition{|s| s.is_a?(Hash) }
19
+ model = DeepPluck::Model.new(self, need_columns: other_args)
20
+ model.add(*hash_args) if hash_args.any?
21
+ return model.load_all.first
22
+ end
23
+ end
@@ -12,7 +12,7 @@ module DeepPluck
12
12
  private
13
13
 
14
14
  def make_data_hash(collection, parent, primary_key, column_name)
15
- return parent.map{|s| [s[primary_key], s]}.to_h if !collection
15
+ return parent.map{|s| [s[primary_key], s] }.to_h if !collection
16
16
  hash = {}
17
17
  parent.each do |model_hash|
18
18
  key = model_hash[primary_key]
@@ -26,8 +26,8 @@ module DeepPluck
26
26
  def assign_values_to_parent(collection, parent, children_hash, column_name, foreign_key, reverse: false)
27
27
  parent.each do |s|
28
28
  next if (id = s[foreign_key]) == nil
29
- left = reverse ? children_hash[id] : s
30
- right = !reverse ? children_hash[id] : s
29
+ left = reverse ? children_hash[id] : s
30
+ right = !reverse ? children_hash[id] : s
31
31
  if collection
32
32
  left[column_name] << right
33
33
  else
@@ -1,16 +1,26 @@
1
- require 'deep_pluck/preloaded_model'
1
+ require 'rails_compatibility'
2
+ require 'rails_compatibility/unscope_where'
2
3
  require 'deep_pluck/data_combiner'
4
+
3
5
  module DeepPluck
4
6
  class Model
5
7
  # ----------------------------------------------------------------
6
8
  # ● Initialize
7
9
  # ----------------------------------------------------------------
8
- def initialize(relation, parent_association_key = nil, parent_model = nil, preloaded_model: nil)
9
- @relation = relation
10
- @preloaded_model = preloaded_model
10
+ def initialize(relation, parent_association_key = nil, parent_model = nil, need_columns: [])
11
+ if relation.is_a?(ActiveRecord::Base)
12
+ @model = relation
13
+ @relation = nil
14
+ @klass = @model.class
15
+ else
16
+ @model = nil
17
+ @relation = relation
18
+ @klass = @relation.klass
19
+ end
20
+
11
21
  @parent_association_key = parent_association_key
12
22
  @parent_model = parent_model
13
- @need_columns = []
23
+ @need_columns = need_columns
14
24
  @associations = {}
15
25
  end
16
26
 
@@ -18,9 +28,9 @@ module DeepPluck
18
28
  # ● Reader
19
29
  # ----------------------------------------------------------------
20
30
  def get_reflect(association_key)
21
- @relation.klass.reflect_on_association(association_key.to_sym) || # add to_sym since rails 3 only support symbol
31
+ @klass.reflect_on_association(association_key.to_sym) || # add to_sym since rails 3 only support symbol
22
32
  fail(ActiveRecord::ConfigurationError, "ActiveRecord::ConfigurationError: Association named \
23
- '#{association_key}' was not found on #{@relation.klass.name}; perhaps you misspelled it?"
33
+ '#{association_key}' was not found on #{@klass.name}; perhaps you misspelled it?"
24
34
  )
25
35
  end
26
36
 
@@ -43,6 +53,7 @@ module DeepPluck
43
53
  end
44
54
 
45
55
  def get_foreign_key(reflect, reverse: false, with_table_name: false)
56
+ reflect = reflect.chain.last
46
57
  if reverse and (table_name = get_join_table(reflect)) # reverse = parent
47
58
  key = reflect.chain.last.foreign_key
48
59
  else
@@ -53,6 +64,14 @@ module DeepPluck
53
64
  return key.to_s # key may be symbol if specify foreign_key in association options
54
65
  end
55
66
 
67
+ def get_association_scope(reflect)
68
+ RailsCompatibility.unscope_where(reflect.association_class.new({}, reflect).send(:association_scope))
69
+ end
70
+
71
+ def use_association_to_query?(reflect)
72
+ reflect.through_reflection && reflect.chain.first.macro == :has_one
73
+ end
74
+
56
75
  # ----------------------------------------------------------------
57
76
  # ● Contruction OPs
58
77
  # ----------------------------------------------------------------
@@ -60,7 +79,7 @@ module DeepPluck
60
79
  private
61
80
 
62
81
  def add_need_column(column)
63
- @need_columns << column.to_s
82
+ @need_columns << column
64
83
  end
65
84
 
66
85
  def add_association(hash)
@@ -92,20 +111,38 @@ module DeepPluck
92
111
  def do_query(parent, reflect, relation)
93
112
  parent_key = get_foreign_key(reflect)
94
113
  relation_key = get_foreign_key(reflect, reverse: true, with_table_name: true)
95
- ids = parent.map{|s| s[parent_key]}
114
+ ids = parent.map{|s| s[parent_key] }
96
115
  ids.uniq!
97
116
  ids.compact!
98
117
  relation = with_conditions(reflect, relation)
99
118
  query = { relation_key => ids }
100
119
  query[reflect.type] = reflect.active_record.to_s if reflect.type
101
- return relation.joins(get_join_table(reflect)).where(query)
120
+
121
+ return get_association_scope(reflect).where(query) if use_association_to_query?(reflect)
122
+
123
+ join_table = get_join_table(reflect)
124
+ join_table = backtrace_possible_association(relation, join_table)
125
+
126
+ return relation.joins(join_table).where(query)
127
+ end
128
+
129
+ # Let city has_many :users, through: :schools
130
+ # And the query is: City.deep_pluck('users' => :name)
131
+ # We want to get the users data via `User.joins(:school).where(city_id: city_ids)`
132
+ # But get_join_table(reflect) returns `:schools` not :school
133
+ # No idea how to get the right association, so we try singularize or pluralize it.
134
+ def backtrace_possible_association(relation, join_table)
135
+ return join_table if relation.reflect_on_association(join_table)
136
+ join_table.to_s.singularize.to_sym.tap{|s| return s if relation.reflect_on_association(s) }
137
+ join_table.to_s.pluralize.to_sym.tap{|s| return s if relation.reflect_on_association(s) }
138
+ return nil
102
139
  end
103
140
 
104
141
  def set_includes_data(parent, column_name, model)
105
142
  reflect = get_reflect(column_name)
106
143
  reverse = !reflect.belongs_to?
107
144
  foreign_key = get_foreign_key(reflect, reverse: reverse)
108
- primary_key = get_primary_key(reflect)
145
+ primary_key = get_foreign_key(reflect, reverse: !reverse)
109
146
  children = model.load_data{|relation| do_query(parent, reflect, relation) }
110
147
  # reverse = false: Child.where(:id => parent.pluck(:child_id))
111
148
  # reverse = true : Child.where(:parent_id => parent.pluck(:id))
@@ -129,13 +166,28 @@ module DeepPluck
129
166
  return [*prev_need_columns, *next_need_columns, *@need_columns].uniq(&Helper::TO_KEY_PROC)
130
167
  end
131
168
 
169
+ def pluck_values(columns)
170
+ includes_values = @relation.includes_values
171
+ @relation.includes_values = []
172
+
173
+ result = @relation.pluck_all(*columns)
174
+
175
+ @relation.includes_values = includes_values
176
+ return result
177
+ end
178
+
179
+ def loaded_models
180
+ return [@model] if @model
181
+ return @relation if @relation.loaded
182
+ end
183
+
132
184
  public
133
185
 
134
186
  def load_data
135
187
  columns = get_query_columns
136
188
  key_columns = columns.map(&Helper::TO_KEY_PROC)
137
189
  @relation = yield(@relation) if block_given?
138
- @data = @preloaded_model ? [@preloaded_model.get_hash_data(key_columns)] : @relation.pluck_all(*columns)
190
+ @data = loaded_models ? loaded_models.as_json(root: false, only: key_columns) : pluck_values(columns)
139
191
  if @data.size != 0
140
192
  # for delete_extra_column_data!
141
193
  @extra_columns = key_columns - @need_columns.map(&Helper::TO_KEY_PROC)