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.
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