Created
September 10, 2013 06:23
-
-
Save pdswan/6505651 to your computer and use it in GitHub Desktop.
Experimentation with making a wrapper for ActiveRecord::Relation that can be used like any other enumerable. Starting with select.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ActiveRecordEnumerable | |
def initialize(relation) | |
@relation = relation | |
end | |
def all | |
relation.all | |
end | |
def select(&block) | |
scope = block.call(ScopeBuilder.new) | |
self.class.new(scope.call(relation)) | |
end | |
private | |
attr_reader :relation | |
class ScopeBuilder | |
def method_missing(attr_name) | |
ConditionBuilder.new(attr_name) | |
end | |
end | |
class ConditionBuilder < Struct.new(:attr_name) | |
def ==(value) | |
Conditions::Equality.new(attr_name, value) | |
end | |
def in(value) | |
Conditions::In.new(attr_name, value) | |
end | |
def !=(value) | |
Conditions::Comparison.new(attr_name, value, :not_eq) | |
end | |
def <=(value) | |
Conditions::Comparison.new(attr_name, value, :lte) | |
end | |
def >=(value) | |
Conditions::Comparison.new(attr_name, value, :gte) | |
end | |
def <(value) | |
Conditions::Comparison.new(attr_name, value, :lt) | |
end | |
def >(value) | |
Conditions::Comparison.new(attr_name, value, :gt) | |
end | |
end | |
module Conditions | |
class Equality < Struct.new(:attr_name, :attr_value) | |
def call(relation) | |
relation.where(attr_name => attr_value) | |
end | |
end | |
class In < Equality; end | |
class Comparison < Struct.new(:attr_name, :attr_value, :comparison_method) | |
def call(relation) | |
relation.where(where_condition(relation)) | |
end | |
private | |
def where_condition(relation) | |
relation. | |
arel_table[attr_name]. | |
public_send(comparison_method, attr_value) | |
end | |
end | |
end | |
end | |
require 'pry' | |
require 'active_record' | |
describe ActiveRecordEnumerable do | |
describe "#all" do | |
let(:relation) { double("ActiveRecord::Relation") } | |
let(:all_records) { double("All records") } | |
let(:enumerable) { ActiveRecordEnumerable.new(relation) } | |
it "proxies through the relation" do | |
relation.stub(:all).and_return(all_records) | |
expect(enumerable.all).to eq(all_records) | |
end | |
end | |
end | |
describe ActiveRecordEnumerable do | |
describe "#select" do | |
let(:relation) { double("ActiveRecord::Relation", arel_table: relation_arel_table) } | |
let(:relation_arel_table) { double("ArelTable") } | |
let(:scoped_relation) { double("ActiveRecord::Relation scoped to name") } | |
let!(:enumerable) { ActiveRecordEnumerable.new(relation) } | |
let(:scoped_enumerable) { double("ActiveRecordEnumerable scoped to name") } | |
before do | |
ActiveRecordEnumerable.stub(:new).with(scoped_relation).and_return(scoped_enumerable) | |
end | |
describe "with ==" do | |
before do | |
relation.stub(:where).with(name: 'Billy Bob').and_return(scoped_relation) | |
end | |
it "should return a new relation scoped to the correct conditions" do | |
expect(enumerable.select { |foo| foo.name == 'Billy Bob' }).to eq(scoped_enumerable) | |
end | |
end | |
describe "in" do | |
let(:array) { double("Array") } | |
before do | |
relation.stub(:where).with(name: array).and_return(scoped_relation) | |
end | |
it "should return a new relation scoped to the correct conditions" do | |
expect(enumerable.select { |foo| | |
foo.name.in(array) | |
}).to eq(scoped_enumerable) | |
end | |
end | |
describe "comparison operators" do | |
let(:arel_attribute) { double("Arel::Attributes::Attribute") } | |
let(:date) { double("Date") } | |
let(:arel_node) { double("Arel::Node") } | |
before do | |
relation_arel_table.stub(:[]).with(:created_at).and_return(arel_attribute) | |
arel_attribute.stub(arel_comparison_method).with(date).and_return(arel_node) | |
relation.stub(:where).with(arel_node).and_return(scoped_relation) | |
end | |
describe "<=" do | |
let(:arel_comparison_method) { :lte } | |
it "should return a new relation scoped to the correct conditions" do | |
expect(enumerable.select { |foo| foo.created_at <= date }).to eq(scoped_enumerable) | |
end | |
end | |
describe ">=" do | |
let(:arel_comparison_method) { :gte } | |
it "should return a new relation scoped to the correct conditions" do | |
expect(enumerable.select { |foo| foo.created_at >= date }).to eq(scoped_enumerable) | |
end | |
end | |
describe "<" do | |
let(:arel_comparison_method) { :lt } | |
it "should return a new relation scoped to the correct conditions" do | |
expect(enumerable.select { |foo| foo.created_at < date }).to eq(scoped_enumerable) | |
end | |
end | |
describe ">" do | |
let(:arel_comparison_method) { :gt } | |
it "should return a new relation scoped to the correct conditions" do | |
expect(enumerable.select { |foo| foo.created_at > date }).to eq(scoped_enumerable) | |
end | |
end | |
describe "!=" do | |
let(:arel_comparison_method) { :not_eq } | |
it "should return a new relation scoped to the correct conditions" do | |
expect(enumerable.select { |foo| foo.created_at != date }).to eq(scoped_enumerable) | |
end | |
end | |
end | |
end | |
end | |
describe ActiveRecordEnumerable do | |
class CreateFoos < ActiveRecord::Migration | |
self.verbose = false | |
def up | |
create_table(:foos) do |t| | |
t.column :name, :string | |
t.column :created_at, :datetime | |
end | |
end | |
def down | |
drop_table(:foos) | |
end | |
end | |
class Foo < ActiveRecord::Base; end | |
context "integration" do | |
before do | |
ActiveRecord::Base.establish_connection( | |
adapter: 'mysql2', | |
host: 'localhost', | |
username: 'root', | |
password: '', | |
database: 'active_record_enumerable_test' | |
) | |
end | |
before do | |
CreateFoos.migrate(:up) | |
end | |
after do | |
CreateFoos.migrate(:down) | |
end | |
describe ".scoped" do | |
subject { Foo.scoped } | |
it "should respond to :arel_table" do | |
expect(Foo.scoped).to respond_to(:arel_table) | |
end | |
end | |
#describe "pry" do | |
# it "should pry" do | |
# binding.pry | |
# end | |
#end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
source 'https://rubygems.org' | |
gem 'activerecord', '~> 3.2' | |
gem 'activerecord-mysql2-adapter' | |
group :test do | |
gem 'rspec' | |
gem 'pry' | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment