deep_pluck 1.0.3 → 1.1.0
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 +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
|