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.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +12 -0
- data/.gitignore +10 -9
- data/.rubocop.yml +1227 -1193
- data/.travis.yml +42 -21
- data/CHANGELOG.md +63 -0
- data/CODE_OF_CONDUCT.md +48 -48
- data/LICENSE.txt +21 -21
- data/README.md +172 -145
- data/Rakefile +5 -5
- data/bin/console +3 -3
- data/bin/setup +8 -8
- data/deep_pluck.gemspec +45 -38
- data/gemfiles/3.2.gemfile +11 -14
- data/gemfiles/4.2.gemfile +11 -14
- data/gemfiles/5.0.gemfile +11 -14
- data/gemfiles/5.1.gemfile +11 -14
- data/gemfiles/5.2.gemfile +11 -0
- data/gemfiles/6.0.gemfile +11 -0
- data/gemfiles/6.1.gemfile +11 -0
- data/lib/deep_pluck.rb +23 -24
- data/lib/deep_pluck/data_combiner.rb +3 -3
- data/lib/deep_pluck/model.rb +64 -12
- data/lib/deep_pluck/version.rb +3 -3
- metadata +38 -10
- data/lib/deep_pluck/preloaded_model.rb +0 -12
data/Rakefile
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
3
|
|
4
4
|
Rake::TestTask.new(:test) do |t|
|
5
|
-
t.libs <<
|
6
|
-
t.libs <<
|
5
|
+
t.libs << 'test'
|
6
|
+
t.libs << 'lib'
|
7
7
|
t.test_files = FileList['test/**/*_test.rb']
|
8
8
|
end
|
9
9
|
|
10
|
-
task :
|
10
|
+
task default: :test
|
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
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
|
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
|
data/deep_pluck.gemspec
CHANGED
@@ -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 =
|
8
|
-
spec.version = DeepPluck::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
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
|
26
|
-
spec.bindir =
|
27
|
-
spec.executables = spec.files.grep(%r{^exe/})
|
28
|
-
spec.require_paths = [
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
data/gemfiles/3.2.gemfile
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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: '../'
|
data/gemfiles/4.2.gemfile
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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: '../'
|
data/gemfiles/5.0.gemfile
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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: '../'
|
data/gemfiles/5.1.gemfile
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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: '../'
|
data/lib/deep_pluck.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
|
-
require
|
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
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def deep_pluck(*args)
|
18
|
-
hash_args = args.
|
19
|
-
|
20
|
-
|
21
|
-
model
|
22
|
-
|
23
|
-
|
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
|
30
|
-
right
|
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
|
data/lib/deep_pluck/model.rb
CHANGED
@@ -1,16 +1,26 @@
|
|
1
|
-
require '
|
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,
|
9
|
-
|
10
|
-
|
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
|
-
@
|
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 #{@
|
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
|
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
|
-
|
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 =
|
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 =
|
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)
|