Skip to content

Commit

Permalink
Merge pull request #1 from khiav223577/feature/deep_pluck
Browse files Browse the repository at this point in the history
deep pluck
  • Loading branch information
khiav223577 committed Mar 4, 2017
2 parents 98bee46 + 83a5e95 commit 80ab5a9
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 3 deletions.
1 change: 1 addition & 0 deletions deep_pluck.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "minitest", "~> 5.0"

spec.add_dependency "rails", ">= 3"
spec.add_dependency "pluck_all", "~> 1.2.1"

end
2 changes: 1 addition & 1 deletion gemfiles/3.2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in pluck_all.gemspec

gem "activerecord", "~> 3.2"
gem "pluck_all", "~> 1.2.1"

group :test do
gem "simplecov"
gem "codeclimate-test-reporter", "~> 1.0.0"
gem 'carrierwave', '~> 0.11.0'
end

gemspec :path => "../"
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/4.2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in pluck_all.gemspec

gem "activerecord", "~> 4.2"
gem "pluck_all", "~> 1.2.1"

group :test do
gem "simplecov"
gem "codeclimate-test-reporter", "~> 1.0.0"
gem 'carrierwave', '~> 0.11.0'
end

gemspec :path => "../"
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/5.0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in pluck_all.gemspec

gem "activerecord", "~> 5.0"
gem "pluck_all", "~> 1.2.1"

group :test do
gem "simplecov"
gem "codeclimate-test-reporter", "~> 1.0.0"
gem 'carrierwave', '~> 0.11.0'
end

gemspec :path => "../"
Expand Down
14 changes: 14 additions & 0 deletions lib/deep_pluck.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
require "deep_pluck/version"
require 'deep_pluck/model'
require 'active_record'
require 'pluck_all'

class ActiveRecord::Relation
def deep_pluck(*args)
DeepPluck::Model.new(self).add(args).load_all
end
end

class ActiveRecord::Base
def self.deep_pluck(*args)
self.where('').deep_pluck(*args)
end
end
95 changes: 95 additions & 0 deletions lib/deep_pluck/model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
module DeepPluck
class Model
#---------------------------------------
# Initialize
#---------------------------------------
def initialize(relation, parent_association_key = nil, parent_model = nil)
@relation = relation
@parent_association_key = parent_association_key
@parent_model = parent_model
@need_columns = []
@associations = {}
end
#---------------------------------------
# Reader
#---------------------------------------
def reflect_on_association(association_key)
@relation.klass.reflect_on_association(association_key)
end
def get_foreign_key(association_key, reverse = false)
reflect = reflect_on_association(association_key)
return (reflect.belongs_to? ? 'id' : reflect.foreign_key) if reverse
return (reflect.belongs_to? ? reflect.foreign_key : 'id')
end
#---------------------------------------
# Contruction OPs
#---------------------------------------
private
def add_need_column(column)
@need_columns << column.to_s
end
def add_association(hash)
hash.each do |key, value|
model = (@associations[key] ||= Model.new(reflect_on_association(key).klass.where(''), key, self))
model.add(value)
end
end
public
def add(args)
return self if args == nil
args = [args] if not args.is_a?(Array)
args.each do |arg|
case arg
when Hash ; add_association(arg)
else ; add_need_column(arg)
end
end
return self
end
#---------------------------------------
# Load
#---------------------------------------
private
def set_includes_data(parent, children_store_name, model, order_by = nil)
reflect = reflect_on_association(children_store_name)
if reflect.belongs_to? #Child.where(:id => parent.pluck(:child_id))
children = model.load_data{|relaction| relaction.where(:id => parent.map{|s| s[reflect.foreign_key]}.uniq.compact).order(order_by) }
children_hash = Hash[children.map{|s| [s["id"], s]}]
parent.each{|s|
next if (id = s[reflect.foreign_key]) == nil
s[children_store_name] = children_hash[id]
}
else #Child.where(:parent_id => parent.pluck(:id))
parent.each{|s| s[children_store_name] = [] }
parent_hash = Hash[parent.map{|s| [s["id"], s]}]
children = model.load_data{|relaction| relaction.where(reflect.foreign_key => parent.map{|s| s["id"]}.uniq.compact).order(order_by) }
children.each{|s|
next if (id = s[reflect.foreign_key]) == nil
parent_hash[id][children_store_name] << s
}
end
return children
end
public
def load_data
prev_need_columns = @parent_model.get_foreign_key(@parent_association_key, true) if @parent_model
next_need_columns = @associations.map{|key, _| get_foreign_key(key) }.uniq
all_need_columns = [*prev_need_columns, *next_need_columns, *@need_columns].uniq
@extra_columns = all_need_columns - @need_columns
@relation = yield(@relation) if block_given?
return (@data = @relation.pluck_all(*all_need_columns))
end
def load_all
data = load_data
@associations.each do |key, model|
set_includes_data(data, key, model)
end
delete_extra_column_data!
return data
end
def delete_extra_column_data!
@data.each{|s| s.except!(*@extra_columns) } if @data
@associations.each{|_, model| model.delete_extra_column_data! }
end
end
end
26 changes: 26 additions & 0 deletions test/deep_pluck_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,34 @@ class DeepPluckTest < Minitest::Test
def setup

end

def test_that_it_has_a_version_number
refute_nil ::DeepPluck::VERSION
end

def test_pluck_with_1_level_deep
assert_equal [
{'name' => 'John'},
{'name' => 'Pearl'},
], User.where(:name => %w(John Pearl)).deep_pluck(:name)
end

def test_pluck_with_2_level_deep
assert_equal [
{'name' => 'Pearl' , :posts => [{'name' => "post4"}, {'name' => "post5"}]},
{'name' => 'Kathenrie', :posts => [{'name' => "post6"}]},
], User.where(:name => %w(Pearl Kathenrie)).deep_pluck(:name, :posts => [:name])
assert_equal [
{'name' => 'John' , :contact => [{'address' => "John's Home"}]},
{'name' => 'Pearl', :contact => [{'address' => "Pearl's Home"}]},
], User.where(:name => %w(John Pearl)).deep_pluck(:name, :contact => [:address])
end

def test_pluck_with_2_level_deep_and_reverse_association
assert_equal [
{'name' => 'post4', :user => {'name' => "Pearl"}},
{'name' => 'post5', :user => {'name' => "Pearl"}},
{'name' => 'post6', :user => {'name' => "Kathenrie"}},
], Post.where(:name => %w(post4 post5 post6)).deep_pluck(:name, :user => [:name])
end
end
14 changes: 14 additions & 0 deletions test/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@
t.string :name
t.string :title
end
create_table :contacts, :force => true do |t|
t.integer :user_id
t.string :address
t.string :phone_number
end
end
class User < ActiveRecord::Base
serialize :serialized_attribute, Hash
has_many :posts
has_one :contact
end
class Post < ActiveRecord::Base
belongs_to :user
end
class Contact < ActiveRecord::Base
belongs_to :user
end
users = User.create([
{:name => 'John', :email => 'john@example.com'},
{:name => 'Pearl', :email => 'pearl@example.com', :serialized_attribute => {:testing => true, :deep => {:deep => :deep}}},
Expand All @@ -36,3 +45,8 @@ class Post < ActiveRecord::Base
{:name => 'post5', :title => "Pearl's post2", :user_id => users[1].id},
{:name => 'post6', :title => "Kathenrie's post1", :user_id => users[2].id},
])
Contact.create([
{:address => "John's Home", :phone_number => "0911666888", :user_id => users[0].id},
{:address => "Pearl's Home", :phone_number => "1011-0404-934", :user_id => users[1].id},
{:address => "Kathenrie's Home", :phone_number => "02-254421", :user_id => users[2].id},
])

0 comments on commit 80ab5a9

Please sign in to comment.