deep_pluck 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1193 -0
- data/README.md +5 -0
- data/lib/deep_pluck.rb +8 -0
- data/lib/deep_pluck/data_combiner.rb +7 -4
- data/lib/deep_pluck/model.rb +76 -36
- data/lib/deep_pluck/preloaded_model.rb +12 -0
- data/lib/deep_pluck/version.rb +1 -1
- metadata +5 -3
data/README.md
CHANGED
@@ -48,6 +48,11 @@ User.deep_pluck(:name, :posts => :title)
|
|
48
48
|
# {'name' => 'Jeremy', :posts => [{'title' => 'post3'}]}
|
49
49
|
# ]
|
50
50
|
```
|
51
|
+
### Support plucking at active models
|
52
|
+
```rb
|
53
|
+
user = User.find_by(name: 'David').deep_pluck(:name, :posts => :title)
|
54
|
+
# => {'name' => 'David' , :posts => [{'title' => 'post1'}, {'title' => 'post2'}]}
|
55
|
+
```
|
51
56
|
|
52
57
|
### DRY up Rails/ActiveRecord includes when using as_json
|
53
58
|
|
data/lib/deep_pluck.rb
CHANGED
@@ -13,4 +13,12 @@ class ActiveRecord::Base
|
|
13
13
|
def self.deep_pluck(*args)
|
14
14
|
self.where('').deep_pluck(*args)
|
15
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
|
16
24
|
end
|
@@ -8,20 +8,23 @@ module DeepPluck
|
|
8
8
|
assign_values_to_parent(collection, target, data_hash, column_name, foreign_key, reverse: reverse)
|
9
9
|
return children
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
|
+
private
|
13
|
+
|
12
14
|
def make_data_hash(collection, parent, primary_key, column_name)
|
13
15
|
return parent.map{|s| [s[primary_key], s]}.to_h if !collection
|
14
16
|
hash = {}
|
15
17
|
parent.each do |model_hash|
|
16
18
|
key = model_hash[primary_key]
|
17
|
-
array = (hash[key] ? hash[key][column_name] : []) #share the children if id is duplicated
|
19
|
+
array = (hash[key] ? hash[key][column_name] : []) # share the children if id is duplicated
|
18
20
|
model_hash[column_name] = array
|
19
21
|
hash[key] = model_hash
|
20
22
|
end
|
21
23
|
return hash
|
22
24
|
end
|
25
|
+
|
23
26
|
def assign_values_to_parent(collection, parent, children_hash, column_name, foreign_key, reverse: false)
|
24
|
-
parent.each
|
27
|
+
parent.each do |s|
|
25
28
|
next if (id = s[foreign_key]) == nil
|
26
29
|
left = reverse ? children_hash[id] : s
|
27
30
|
right = !reverse ? children_hash[id] : s
|
@@ -30,7 +33,7 @@ module DeepPluck
|
|
30
33
|
else
|
31
34
|
left[column_name] = right
|
32
35
|
end
|
33
|
-
|
36
|
+
end
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
data/lib/deep_pluck/model.rb
CHANGED
@@ -1,61 +1,77 @@
|
|
1
|
+
require 'deep_pluck/preloaded_model'
|
1
2
|
require 'deep_pluck/data_combiner'
|
2
3
|
module DeepPluck
|
3
4
|
class Model
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(relation, parent_association_key = nil, parent_model = nil)
|
5
|
+
# ----------------------------------------------------------------
|
6
|
+
# ● Initialize
|
7
|
+
# ----------------------------------------------------------------
|
8
|
+
def initialize(relation, parent_association_key = nil, parent_model = nil, preloaded_model: nil)
|
8
9
|
@relation = relation
|
10
|
+
@preloaded_model = preloaded_model
|
9
11
|
@parent_association_key = parent_association_key
|
10
12
|
@parent_model = parent_model
|
11
13
|
@need_columns = []
|
12
14
|
@associations = {}
|
13
15
|
end
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
|
17
|
+
# ----------------------------------------------------------------
|
18
|
+
# ● Reader
|
19
|
+
# ----------------------------------------------------------------
|
17
20
|
def get_reflect(association_key)
|
18
|
-
@relation.klass.reflect_on_association(association_key.to_sym) || #add to_sym since rails 3 only support symbol
|
19
|
-
|
21
|
+
@relation.klass.reflect_on_association(association_key.to_sym) || # add to_sym since rails 3 only support symbol
|
22
|
+
fail(ActiveRecord::ConfigurationError, "ActiveRecord::ConfigurationError: Association named \
|
23
|
+
'#{association_key}' was not found on #{@relation.klass.name}; perhaps you misspelled it?"
|
24
|
+
)
|
20
25
|
end
|
26
|
+
|
21
27
|
def with_conditions(reflect, relation)
|
22
28
|
options = reflect.options
|
23
29
|
relation = relation.instance_exec(&reflect.scope) if reflect.respond_to?(:scope) and reflect.scope
|
24
30
|
relation = relation.where(options[:conditions]) if options[:conditions]
|
25
31
|
return relation
|
26
32
|
end
|
33
|
+
|
27
34
|
def get_join_table(reflect)
|
28
|
-
|
29
|
-
return
|
35
|
+
options = reflect.options
|
36
|
+
return options[:through] if options[:through]
|
37
|
+
return (options[:join_table] || reflect.send(:derive_join_table)) if reflect.macro == :has_and_belongs_to_many
|
30
38
|
return nil
|
31
39
|
end
|
40
|
+
|
32
41
|
def get_primary_key(reflect)
|
33
42
|
return (reflect.belongs_to? ? reflect.klass : reflect.active_record).primary_key
|
34
43
|
end
|
44
|
+
|
35
45
|
def get_foreign_key(reflect, reverse: false, with_table_name: false)
|
36
|
-
if reverse and (table_name = get_join_table(reflect)) #reverse = parent
|
46
|
+
if reverse and (table_name = get_join_table(reflect)) # reverse = parent
|
37
47
|
key = reflect.chain.last.foreign_key
|
38
48
|
else
|
39
49
|
key = (reflect.belongs_to? == reverse ? get_primary_key(reflect) : reflect.foreign_key)
|
40
50
|
table_name = (reverse ? reflect.klass : reflect.active_record).table_name
|
41
51
|
end
|
42
52
|
return "#{table_name}.#{key}" if with_table_name
|
43
|
-
return key.to_s #key may be symbol if specify foreign_key in association options
|
53
|
+
return key.to_s # key may be symbol if specify foreign_key in association options
|
44
54
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
|
56
|
+
# ----------------------------------------------------------------
|
57
|
+
# ● Contruction OPs
|
58
|
+
# ----------------------------------------------------------------
|
59
|
+
|
60
|
+
private
|
61
|
+
|
49
62
|
def add_need_column(column)
|
50
63
|
@need_columns << column.to_s
|
51
64
|
end
|
65
|
+
|
52
66
|
def add_association(hash)
|
53
67
|
hash.each do |key, value|
|
54
68
|
model = (@associations[key] ||= Model.new(get_reflect(key).klass.where(''), key, self))
|
55
69
|
model.add(value)
|
56
70
|
end
|
57
71
|
end
|
58
|
-
|
72
|
+
|
73
|
+
public
|
74
|
+
|
59
75
|
def add(args)
|
60
76
|
return self if args == nil
|
61
77
|
args = [args] if not args.is_a?(Array)
|
@@ -67,10 +83,12 @@ module DeepPluck
|
|
67
83
|
end
|
68
84
|
return self
|
69
85
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
86
|
+
|
87
|
+
# ----------------------------------------------------------------
|
88
|
+
# ● Load
|
89
|
+
# ----------------------------------------------------------------
|
90
|
+
private
|
91
|
+
|
74
92
|
def do_query(parent, reflect, relation)
|
75
93
|
parent_key = get_foreign_key(reflect)
|
76
94
|
relation_key = get_foreign_key(reflect, reverse: true, with_table_name: true)
|
@@ -82,48 +100,70 @@ module DeepPluck
|
|
82
100
|
query[reflect.type] = reflect.active_record.to_s if reflect.type
|
83
101
|
return relation.joins(get_join_table(reflect)).where(query)
|
84
102
|
end
|
103
|
+
|
85
104
|
def set_includes_data(parent, column_name, model)
|
86
105
|
reflect = get_reflect(column_name)
|
87
106
|
reverse = !reflect.belongs_to?
|
88
107
|
foreign_key = get_foreign_key(reflect, reverse: reverse)
|
89
108
|
primary_key = get_primary_key(reflect)
|
90
109
|
children = model.load_data{|relation| do_query(parent, reflect, relation) }
|
91
|
-
#reverse = false: Child.where(:id => parent.pluck(:child_id))
|
92
|
-
#reverse = true : Child.where(:parent_id => parent.pluck(:id))
|
93
|
-
return DataCombiner.combine_data(
|
110
|
+
# reverse = false: Child.where(:id => parent.pluck(:child_id))
|
111
|
+
# reverse = true : Child.where(:parent_id => parent.pluck(:id))
|
112
|
+
return DataCombiner.combine_data(
|
113
|
+
parent,
|
114
|
+
children,
|
115
|
+
primary_key,
|
116
|
+
column_name,
|
117
|
+
foreign_key,
|
118
|
+
reverse,
|
119
|
+
reflect.collection?,
|
120
|
+
)
|
94
121
|
end
|
95
|
-
|
96
|
-
def
|
97
|
-
|
122
|
+
|
123
|
+
def get_query_columns
|
124
|
+
if @parent_model
|
125
|
+
parent_reflect = @parent_model.get_reflect(@parent_association_key)
|
126
|
+
prev_need_columns = @parent_model.get_foreign_key(parent_reflect, reverse: true, with_table_name: true)
|
127
|
+
end
|
98
128
|
next_need_columns = @associations.map{|key, _| get_foreign_key(get_reflect(key), with_table_name: true) }.uniq
|
99
|
-
|
129
|
+
return [*prev_need_columns, *next_need_columns, *@need_columns].uniq(&Helper::TO_KEY_PROC)
|
130
|
+
end
|
131
|
+
|
132
|
+
public
|
133
|
+
|
134
|
+
def load_data
|
135
|
+
columns = get_query_columns
|
136
|
+
key_columns = columns.map(&Helper::TO_KEY_PROC)
|
100
137
|
@relation = yield(@relation) if block_given?
|
101
|
-
@data = @relation.pluck_all(*
|
138
|
+
@data = @preloaded_model ? [@preloaded_model.get_hash_data(key_columns)] : @relation.pluck_all(*columns)
|
102
139
|
if @data.size != 0
|
103
|
-
#for delete_extra_column_data!
|
104
|
-
@extra_columns =
|
140
|
+
# for delete_extra_column_data!
|
141
|
+
@extra_columns = key_columns - @need_columns.map(&Helper::TO_KEY_PROC)
|
105
142
|
@associations.each do |key, model|
|
106
143
|
set_includes_data(@data, key, model)
|
107
144
|
end
|
108
145
|
end
|
109
146
|
return @data
|
110
147
|
end
|
148
|
+
|
111
149
|
def load_all
|
112
150
|
load_data
|
113
151
|
delete_extra_column_data!
|
114
152
|
return @data
|
115
153
|
end
|
154
|
+
|
116
155
|
def delete_extra_column_data!
|
117
156
|
return if @data.blank?
|
118
157
|
@data.each{|s| s.except!(*@extra_columns) }
|
119
158
|
@associations.each{|_, model| model.delete_extra_column_data! }
|
120
159
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
160
|
+
|
161
|
+
# ----------------------------------------------------------------
|
162
|
+
# ● Helper methods
|
163
|
+
# ----------------------------------------------------------------
|
124
164
|
module Helper
|
125
165
|
TO_KEY_PROC = proc{|s| Helper.column_to_key(s) }
|
126
|
-
def self.column_to_key(key) #user_achievements.user_id => user_id
|
166
|
+
def self.column_to_key(key) # user_achievements.user_id => user_id
|
127
167
|
key = key[/(\w+)[^\w]*\z/]
|
128
168
|
key.gsub!(/[^\w]+/, '')
|
129
169
|
return key
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module DeepPluck
|
2
|
+
class PreloadedModel
|
3
|
+
def initialize(active_model, need_columns)
|
4
|
+
@active_model = active_model
|
5
|
+
@need_columns = need_columns
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_hash_data(extra_columns)
|
9
|
+
@active_model.as_json(root: false, only: @need_columns + extra_columns)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/deep_pluck/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deep_pluck
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- khiav reoy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -104,6 +104,7 @@ extensions: []
|
|
104
104
|
extra_rdoc_files: []
|
105
105
|
files:
|
106
106
|
- ".gitignore"
|
107
|
+
- ".rubocop.yml"
|
107
108
|
- ".travis.yml"
|
108
109
|
- CODE_OF_CONDUCT.md
|
109
110
|
- LICENSE.txt
|
@@ -119,6 +120,7 @@ files:
|
|
119
120
|
- lib/deep_pluck.rb
|
120
121
|
- lib/deep_pluck/data_combiner.rb
|
121
122
|
- lib/deep_pluck/model.rb
|
123
|
+
- lib/deep_pluck/preloaded_model.rb
|
122
124
|
- lib/deep_pluck/version.rb
|
123
125
|
homepage: https://github.com/khiav223577/deep_pluck
|
124
126
|
licenses:
|
@@ -140,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
142
|
version: '0'
|
141
143
|
requirements: []
|
142
144
|
rubyforge_project:
|
143
|
-
rubygems_version: 2.6.
|
145
|
+
rubygems_version: 2.6.13
|
144
146
|
signing_key:
|
145
147
|
specification_version: 4
|
146
148
|
summary: Use deep_pluck as a shortcut to select one or more attributes and include
|