deep_pluck 1.1.0 → 1.1.5

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