deep_pluck 1.1.2 → 1.1.3

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