deep_pluck 1.1.2 → 1.1.3

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.
File without changes
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,44 +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 = '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
- 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,11 +1,11 @@
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
+ 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,11 +1,11 @@
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
+ 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,11 +1,11 @@
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
+ 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,11 +1,11 @@
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: '../'
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: '../'
@@ -1,11 +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: '../'
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: '../'
@@ -1,11 +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: '../'
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: '../'
@@ -1,23 +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
- 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
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
@@ -1,196 +1,209 @@
1
- require 'deep_pluck/data_combiner'
2
-
3
- module DeepPluck
4
- class Model
5
- # ----------------------------------------------------------------
6
- # ● Initialize
7
- # ----------------------------------------------------------------
8
- def initialize(relation, parent_association_key = nil, parent_model = nil, need_columns: [])
9
- if relation.is_a?(ActiveRecord::Base)
10
- @model = relation
11
- @relation = nil
12
- @klass = @model.class
13
- else
14
- @model = nil
15
- @relation = relation
16
- @klass = @relation.klass
17
- end
18
-
19
- @parent_association_key = parent_association_key
20
- @parent_model = parent_model
21
- @need_columns = need_columns
22
- @associations = {}
23
- end
24
-
25
- # ----------------------------------------------------------------
26
- # ● Reader
27
- # ----------------------------------------------------------------
28
- def get_reflect(association_key)
29
- @klass.reflect_on_association(association_key.to_sym) || # add to_sym since rails 3 only support symbol
30
- fail(ActiveRecord::ConfigurationError, "ActiveRecord::ConfigurationError: Association named \
31
- '#{association_key}' was not found on #{@klass.name}; perhaps you misspelled it?"
32
- )
33
- end
34
-
35
- def with_conditions(reflect, relation)
36
- options = reflect.options
37
- relation = relation.instance_exec(&reflect.scope) if reflect.respond_to?(:scope) and reflect.scope
38
- relation = relation.where(options[:conditions]) if options[:conditions]
39
- return relation
40
- end
41
-
42
- def get_join_table(reflect)
43
- options = reflect.options
44
- return options[:through] if options[:through]
45
- return (options[:join_table] || reflect.send(:derive_join_table)) if reflect.macro == :has_and_belongs_to_many
46
- return nil
47
- end
48
-
49
- def get_primary_key(reflect)
50
- return (reflect.belongs_to? ? reflect.klass : reflect.active_record).primary_key
51
- end
52
-
53
- def get_foreign_key(reflect, reverse: false, with_table_name: false)
54
- if reverse and (table_name = get_join_table(reflect)) # reverse = parent
55
- key = reflect.chain.last.foreign_key
56
- else
57
- key = (reflect.belongs_to? == reverse ? get_primary_key(reflect) : reflect.foreign_key)
58
- table_name = (reverse ? reflect.klass : reflect.active_record).table_name
59
- end
60
- return "#{table_name}.#{key}" if with_table_name
61
- return key.to_s # key may be symbol if specify foreign_key in association options
62
- end
63
-
64
- # ----------------------------------------------------------------
65
- # ● Contruction OPs
66
- # ----------------------------------------------------------------
67
-
68
- private
69
-
70
- def add_need_column(column)
71
- @need_columns << column.to_s
72
- end
73
-
74
- def add_association(hash)
75
- hash.each do |key, value|
76
- model = (@associations[key] ||= Model.new(get_reflect(key).klass.where(''), key, self))
77
- model.add(value)
78
- end
79
- end
80
-
81
- public
82
-
83
- def add(args)
84
- return self if args == nil
85
- args = [args] if not args.is_a?(Array)
86
- args.each do |arg|
87
- case arg
88
- when Hash ; add_association(arg)
89
- else ; add_need_column(arg)
90
- end
91
- end
92
- return self
93
- end
94
-
95
- # ----------------------------------------------------------------
96
- # Load
97
- # ----------------------------------------------------------------
98
- private
99
-
100
- def do_query(parent, reflect, relation)
101
- parent_key = get_foreign_key(reflect)
102
- relation_key = get_foreign_key(reflect, reverse: true, with_table_name: true)
103
- ids = parent.map{|s| s[parent_key] }
104
- ids.uniq!
105
- ids.compact!
106
- relation = with_conditions(reflect, relation)
107
- query = { relation_key => ids }
108
- query[reflect.type] = reflect.active_record.to_s if reflect.type
109
- return relation.joins(get_join_table(reflect)).where(query)
110
- end
111
-
112
- def set_includes_data(parent, column_name, model)
113
- reflect = get_reflect(column_name)
114
- reverse = !reflect.belongs_to?
115
- foreign_key = get_foreign_key(reflect, reverse: reverse)
116
- primary_key = get_primary_key(reflect)
117
- children = model.load_data{|relation| do_query(parent, reflect, relation) }
118
- # reverse = false: Child.where(:id => parent.pluck(:child_id))
119
- # reverse = true : Child.where(:parent_id => parent.pluck(:id))
120
- return DataCombiner.combine_data(
121
- parent,
122
- children,
123
- primary_key,
124
- column_name,
125
- foreign_key,
126
- reverse,
127
- reflect.collection?,
128
- )
129
- end
130
-
131
- def get_query_columns
132
- if @parent_model
133
- parent_reflect = @parent_model.get_reflect(@parent_association_key)
134
- prev_need_columns = @parent_model.get_foreign_key(parent_reflect, reverse: true, with_table_name: true)
135
- end
136
- next_need_columns = @associations.map{|key, _| get_foreign_key(get_reflect(key), with_table_name: true) }.uniq
137
- return [*prev_need_columns, *next_need_columns, *@need_columns].uniq(&Helper::TO_KEY_PROC)
138
- end
139
-
140
- def pluck_values(columns)
141
- includes_values = @relation.includes_values
142
- @relation.includes_values = []
143
-
144
- result = @relation.pluck_all(*columns)
145
-
146
- @relation.includes_values = includes_values
147
- return result
148
- end
149
-
150
- def loaded_models
151
- return [@model] if @model
152
- return @relation if @relation.loaded
153
- end
154
-
155
- public
156
-
157
- def load_data
158
- columns = get_query_columns
159
- key_columns = columns.map(&Helper::TO_KEY_PROC)
160
- @relation = yield(@relation) if block_given?
161
- @data = loaded_models ? loaded_models.as_json(root: false, only: key_columns) : pluck_values(columns)
162
- if @data.size != 0
163
- # for delete_extra_column_data!
164
- @extra_columns = key_columns - @need_columns.map(&Helper::TO_KEY_PROC)
165
- @associations.each do |key, model|
166
- set_includes_data(@data, key, model)
167
- end
168
- end
169
- return @data
170
- end
171
-
172
- def load_all
173
- load_data
174
- delete_extra_column_data!
175
- return @data
176
- end
177
-
178
- def delete_extra_column_data!
179
- return if @data.blank?
180
- @data.each{|s| s.except!(*@extra_columns) }
181
- @associations.each{|_, model| model.delete_extra_column_data! }
182
- end
183
-
184
- # ----------------------------------------------------------------
185
- # ● Helper methods
186
- # ----------------------------------------------------------------
187
- module Helper
188
- TO_KEY_PROC = proc{|s| Helper.column_to_key(s) }
189
- def self.column_to_key(key) # user_achievements.user_id => user_id
190
- key = key[/(\w+)[^\w]*\z/]
191
- key.gsub!(/[^\w]+/, '')
192
- return key
193
- end
194
- end
195
- end
196
- end
1
+ require 'rails_compatibility'
2
+ require 'rails_compatibility/unscope_where'
3
+ require 'deep_pluck/data_combiner'
4
+
5
+ module DeepPluck
6
+ class Model
7
+ # ----------------------------------------------------------------
8
+ # Initialize
9
+ # ----------------------------------------------------------------
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
+
21
+ @parent_association_key = parent_association_key
22
+ @parent_model = parent_model
23
+ @need_columns = need_columns
24
+ @associations = {}
25
+ end
26
+
27
+ # ----------------------------------------------------------------
28
+ # ● Reader
29
+ # ----------------------------------------------------------------
30
+ def get_reflect(association_key)
31
+ @klass.reflect_on_association(association_key.to_sym) || # add to_sym since rails 3 only support symbol
32
+ fail(ActiveRecord::ConfigurationError, "ActiveRecord::ConfigurationError: Association named \
33
+ '#{association_key}' was not found on #{@klass.name}; perhaps you misspelled it?"
34
+ )
35
+ end
36
+
37
+ def with_conditions(reflect, relation)
38
+ options = reflect.options
39
+ relation = relation.instance_exec(&reflect.scope) if reflect.respond_to?(:scope) and reflect.scope
40
+ relation = relation.where(options[:conditions]) if options[:conditions]
41
+ return relation
42
+ end
43
+
44
+ def get_join_table(reflect)
45
+ options = reflect.options
46
+ return options[:through] if options[:through]
47
+ return (options[:join_table] || reflect.send(:derive_join_table)) if reflect.macro == :has_and_belongs_to_many
48
+ return nil
49
+ end
50
+
51
+ def get_primary_key(reflect)
52
+ return (reflect.belongs_to? ? reflect.klass : reflect.active_record).primary_key
53
+ end
54
+
55
+ def get_foreign_key(reflect, reverse: false, with_table_name: false)
56
+ reflect = reflect.chain.last
57
+ if reverse and (table_name = get_join_table(reflect)) # reverse = parent
58
+ key = reflect.chain.last.foreign_key
59
+ else
60
+ key = (reflect.belongs_to? == reverse ? get_primary_key(reflect) : reflect.foreign_key)
61
+ table_name = (reverse ? reflect.klass : reflect.active_record).table_name
62
+ end
63
+ return "#{table_name}.#{key}" if with_table_name
64
+ return key.to_s # key may be symbol if specify foreign_key in association options
65
+ end
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
+
75
+ # ----------------------------------------------------------------
76
+ # Contruction OPs
77
+ # ----------------------------------------------------------------
78
+
79
+ private
80
+
81
+ def add_need_column(column)
82
+ @need_columns << column
83
+ end
84
+
85
+ def add_association(hash)
86
+ hash.each do |key, value|
87
+ model = (@associations[key] ||= Model.new(get_reflect(key).klass.where(''), key, self))
88
+ model.add(value)
89
+ end
90
+ end
91
+
92
+ public
93
+
94
+ def add(args)
95
+ return self if args == nil
96
+ args = [args] if not args.is_a?(Array)
97
+ args.each do |arg|
98
+ case arg
99
+ when Hash ; add_association(arg)
100
+ else ; add_need_column(arg)
101
+ end
102
+ end
103
+ return self
104
+ end
105
+
106
+ # ----------------------------------------------------------------
107
+ # Load
108
+ # ----------------------------------------------------------------
109
+ private
110
+
111
+ def do_query(parent, reflect, relation)
112
+ parent_key = get_foreign_key(reflect)
113
+ relation_key = get_foreign_key(reflect, reverse: true, with_table_name: true)
114
+ ids = parent.map{|s| s[parent_key] }
115
+ ids.uniq!
116
+ ids.compact!
117
+ relation = with_conditions(reflect, relation)
118
+ query = { relation_key => ids }
119
+ query[reflect.type] = reflect.active_record.to_s if reflect.type
120
+
121
+ return get_association_scope(reflect).where(query) if use_association_to_query?(reflect)
122
+ return relation.joins(get_join_table(reflect)).where(query)
123
+ end
124
+
125
+ def set_includes_data(parent, column_name, model)
126
+ reflect = get_reflect(column_name)
127
+ reverse = !reflect.belongs_to?
128
+ foreign_key = get_foreign_key(reflect, reverse: reverse)
129
+ primary_key = get_foreign_key(reflect, reverse: !reverse)
130
+ children = model.load_data{|relation| do_query(parent, reflect, relation) }
131
+ # reverse = false: Child.where(:id => parent.pluck(:child_id))
132
+ # reverse = true : Child.where(:parent_id => parent.pluck(:id))
133
+ return DataCombiner.combine_data(
134
+ parent,
135
+ children,
136
+ primary_key,
137
+ column_name,
138
+ foreign_key,
139
+ reverse,
140
+ reflect.collection?,
141
+ )
142
+ end
143
+
144
+ def get_query_columns
145
+ if @parent_model
146
+ parent_reflect = @parent_model.get_reflect(@parent_association_key)
147
+ prev_need_columns = @parent_model.get_foreign_key(parent_reflect, reverse: true, with_table_name: true)
148
+ end
149
+ next_need_columns = @associations.map{|key, _| get_foreign_key(get_reflect(key), with_table_name: true) }.uniq
150
+ return [*prev_need_columns, *next_need_columns, *@need_columns].uniq(&Helper::TO_KEY_PROC)
151
+ end
152
+
153
+ def pluck_values(columns)
154
+ includes_values = @relation.includes_values
155
+ @relation.includes_values = []
156
+
157
+ result = @relation.pluck_all(*columns)
158
+
159
+ @relation.includes_values = includes_values
160
+ return result
161
+ end
162
+
163
+ def loaded_models
164
+ return [@model] if @model
165
+ return @relation if @relation.loaded
166
+ end
167
+
168
+ public
169
+
170
+ def load_data
171
+ columns = get_query_columns
172
+ key_columns = columns.map(&Helper::TO_KEY_PROC)
173
+ @relation = yield(@relation) if block_given?
174
+ @data = loaded_models ? loaded_models.as_json(root: false, only: key_columns) : pluck_values(columns)
175
+ if @data.size != 0
176
+ # for delete_extra_column_data!
177
+ @extra_columns = key_columns - @need_columns.map(&Helper::TO_KEY_PROC)
178
+ @associations.each do |key, model|
179
+ set_includes_data(@data, key, model)
180
+ end
181
+ end
182
+ return @data
183
+ end
184
+
185
+ def load_all
186
+ load_data
187
+ delete_extra_column_data!
188
+ return @data
189
+ end
190
+
191
+ def delete_extra_column_data!
192
+ return if @data.blank?
193
+ @data.each{|s| s.except!(*@extra_columns) }
194
+ @associations.each{|_, model| model.delete_extra_column_data! }
195
+ end
196
+
197
+ # ----------------------------------------------------------------
198
+ # ● Helper methods
199
+ # ----------------------------------------------------------------
200
+ module Helper
201
+ TO_KEY_PROC = proc{|s| Helper.column_to_key(s) }
202
+ def self.column_to_key(key) # user_achievements.user_id => user_id
203
+ key = key[/(\w+)[^\w]*\z/]
204
+ key.gsub!(/[^\w]+/, '')
205
+ return key
206
+ end
207
+ end
208
+ end
209
+ end