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.
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
 
@@ -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
- private
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{|s|
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
@@ -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
- # Initialize
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
- # Reader
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
- raise(ActiveRecord::ConfigurationError, "ActiveRecord::ConfigurationError: Association named '#{association_key}' was not found on #{@relation.klass.name}; perhaps you misspelled it?")
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
- return reflect.options[:through] if reflect.options[:through]
29
- return (reflect.options[:join_table] || reflect.send(:derive_join_table)) if reflect.macro == :has_and_belongs_to_many
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
- # Contruction OPs
47
- #---------------------------------------
48
- private
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
- public
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
- # Load
72
- #---------------------------------------
73
- private
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(parent, children, primary_key, column_name, foreign_key, reverse, reflect.collection?)
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
- public
96
- def load_data
97
- prev_need_columns = @parent_model.get_foreign_key(@parent_model.get_reflect(@parent_association_key), reverse: true, with_table_name: true) if @parent_model
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
- all_need_columns = [*prev_need_columns, *next_need_columns, *@need_columns].uniq(&Helper::TO_KEY_PROC)
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(*all_need_columns)
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 = all_need_columns.map(&Helper::TO_KEY_PROC) - @need_columns.map(&Helper::TO_KEY_PROC)
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
- # Helper methods
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
@@ -1,3 +1,3 @@
1
1
  module DeepPluck
2
- VERSION = "1.0.3"
2
+ VERSION = "1.1.0"
3
3
  end
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.3
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: 2017-06-30 00:00:00.000000000 Z
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.8
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