Permalink
Browse files

Avoide complex parameter nesting

  • Loading branch information...
1 parent 1f062a1 commit 56a29418e1dbedfd6930ff46f6562b10fe4343d4 @dim dim committed Jul 25, 2011
View
2 Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- constrainable (0.3.1)
+ constrainable (0.4.0)
abstract
activerecord (~> 3.0.0)
activesupport (~> 3.0.0)
View
2 constrainable.gemspec
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
s.name = "constrainable"
s.summary = "Simple filtering for ActiveRecord"
s.description = "Sanitizes simple and readable query parameters -great for building APIs & HTML filters"
- s.version = '0.3.1'
+ s.version = '0.4.0'
s.authors = ["Dimitrij Denissenko"]
s.email = "dimitrij@blacksquaremedia.com"
View
16 lib/bsm/constrainable/field/base.rb
@@ -35,15 +35,15 @@ def initialize(name, options = {})
end
# Merge params into a relation
- def merge(relation, params)
- params.slice(*operators).each do |operator, value|
- operation = Bsm::Constrainable::Operation.new(operator, value, relation, self)
- next if operation.clause.nil?
+ def merge(relation, operator, value)
+ operator = operator.to_sym
+ return relation unless operators.include?(operator)
- relation = relation.instance_eval(&scope) if scope
- relation = relation.where(operation.clause)
- end
- relation
+ operation = Bsm::Constrainable::Operation.new(operator, value, relation, self)
+ return relation if operation.clause.nil?
+
+ relation = relation.instance_eval(&scope) if scope
+ relation.where(operation.clause)
end
def convert(value)
View
26 lib/bsm/constrainable/filter_set.rb
@@ -6,41 +6,39 @@ class Bsm::Constrainable::FilterSet < Hash
def initialize(schema, params = {})
@schema = schema
- normalized_hash(params).slice(*schema.keys).each do |name, part|
- update name => part.symbolize_keys if part.is_a?(Hash)
+ normalized_hash(params).each do |key, value|
+ name, op = key.split('__')
+ update(key => value) if valid_operation?(name, op)
end
end
def merge(scoped)
- each do |name, part|
+ each do |key, value|
+ name, op = key.split('__')
schema[name].each do |field|
- scoped = field.merge(scoped, part)
+ scoped = field.merge(scoped, op, value)
end
end
scoped
end
def respond_to?(sym, *)
- name, operator = sym.to_s.sub(NAME_OP, ''), $1
- super || (operator.nil? && schema.key?(name)) || valid_operator?(name, operator)
+ name, op = sym.to_s.split('__')
+ super || valid_operation?(name, op)
end
private
- NAME_OP = /\[(\w+)\]$/.freeze
def method_missing(sym, *args)
- name, operator = sym.to_s.sub(NAME_OP, ''), $1
-
- if (operator.nil? && schema.key?(name))
- self[name]
- elsif valid_operator?(name, operator)
- self[name].try(:[], operator.to_sym)
+ name, op = sym.to_s.split('__')
+ if valid_operation?(name, op)
+ self[sym.to_s]
else
super
end
end
- def valid_operator?(name, operator)
+ def valid_operation?(name, operator)
return false unless operator.present? && schema.key?(name.to_s)
schema[name].any? do |field|
View
4 lib/bsm/constrainable/relation.rb
@@ -3,10 +3,10 @@ module Bsm::Constrainable::Relation
# Apply contraints. Example:
#
- # Post.constrain("created_at" => { "lt" => "2011-01-01" }})
+ # Post.constrain("created_at__lt" => "2011-01-01")
#
# # Use "custom" constraints
- # Post.constrain(:custom, "created_at" => { "lt" => "2011-01-01" }})
+ # Post.constrain(:custom, "created_at__lt" => "2011-01-01")
#
# # Combine it with relations & scopes
# Post.archived.includes(:author).constrain(params[:where]).paginate :page => 1
View
25 spec/bsm/constrainable/field/base_spec.rb
@@ -15,8 +15,8 @@ def field(name)
Post._constrainable[:default][name].first
end
- def merge(name, params = {})
- field(name).merge(Post.scoped, params)
+ def merge(name, operator, value)
+ field(name).merge(Post.scoped, operator, value)
end
it { described_class.should have(7).operators }
@@ -42,15 +42,22 @@ def merge(name, params = {})
integer.convert([1, 'a', 2]).should == [1, nil, 2]
end
- it 'should parse params and merge valid scopes' do
- merge("id", :in => [1, 2, 3]).where_sql.clean_sql.should == "WHERE posts.id IN (1, 2, 3)"
- merge("author_id", :in => [1, 3]).where_sql.clean_sql.should == "WHERE posts.author_id IN (1, 3)"
- merge("created", :between => "2010-01-01..2010-02-01", :gt => "2010-01-01").where_values.map(&:to_sql).map(&:clean_sql).
- should =~ ["posts.created_at >= '2010-01-01 00:00:00' AND posts.created_at <= '2010-02-01 00:00:00'", "posts.created_at > '2010-01-01 00:00:00'"]
+ it 'should merge valid scopes' do
+ merge("id", :in, [1, 2, 3]).where_sql.clean_sql.should == "WHERE posts.id IN (1, 2, 3)"
+ merge("author_id", :in, [1, 3]).where_sql.clean_sql.should == "WHERE posts.author_id IN (1, 3)"
+
+ merge("created", :between, "2010-01-01..2010-02-01").where_sql.clean_sql.should == "WHERE (posts.created_at >= '2010-01-01 00:00:00' AND posts.created_at <= '2010-02-01 00:00:00')"
+ merge("created", :gt, "2010-01-01").where_sql.clean_sql.should == "WHERE (posts.created_at > '2010-01-01 00:00:00')"
+ end
+
+ it 'should skip invalid scopes' do
+ merge("id", :invalid, "1").where_values.should == []
+ merge("id", :lt, "1").where_values.should == []
+ merge("id", :in, []).where_values.should == []
end
- it 'should include custom scopes' do
- rel = merge("author_name", :eq => "Alice")
+ it 'should merge/include custom scopes' do
+ rel = merge("author_name", 'eq', "Alice")
rel.includes_values.should == [:author]
rel.where_sql.clean_sql.should == "WHERE authors.name = 'Alice'"
rel.first.should == posts(:article)
View
24 spec/bsm/constrainable/filter_set_spec.rb
@@ -11,7 +11,7 @@
end
subject do
- filter_set 'author_id' => { 'in' => ['1', '2', '3'], 'lt' => '4' }, 'created' => { 'gt' => '2010-10-10' }, 'invalid' => "TRUE", 'empty' => {}
+ filter_set 'author_id__in' => ['1', '2', '3'], 'author_id__lt' => '4', 'created__gt' => '2010-10-10', 'invalid' => "TRUE", 'empty' => {}
end
def filter_set(params = nil)
@@ -25,7 +25,7 @@ def filter_set(params = nil)
end
it 'should normalize params' do
- subject.should == {"author_id"=>{:in=>["1", "2", "3"], :lt=>"4"}, "created"=>{:gt=>"2010-10-10"}}
+ subject.should == { "author_id__in" => ["1", "2", "3"], "created__gt" => "2010-10-10" }
end
it 'should merge params into relations' do
@@ -34,18 +34,18 @@ def filter_set(params = nil)
end
it 'should have accessors to schema keys' do
- subject.should respond_to(:author_id)
- subject.author_id.should == { :in=>["1", "2", "3"], :lt=>"4" }
+ subject.should respond_to(:author_id__in)
+ subject.author_id__in.should == ["1", "2", "3"]
end
it 'should have form-processable accessors' do
- subject.should respond_to(:"author_id[in]")
- subject.send(:"author_id[in]").should == ["1", "2", "3"]
+ subject.should respond_to(:"author_id__in")
+ subject.send(:"author_id__in").should == ["1", "2", "3"]
- subject.should respond_to("created[between]")
- subject.send("created[between]").should be_nil
+ subject.should respond_to("created__between")
+ subject.send("created__between").should be_nil
- subject.should_not respond_to("author[gt]")
+ subject.should_not respond_to("author__lt")
end
describe "in forms" do
@@ -55,7 +55,7 @@ def filter_set(params = nil)
end
let :filters do
- filter_set 'author_id' => { 'in' => ['1', '2'] }, 'created' => { 'between' => ['2010-10-10', '2011-11-11'] }
+ filter_set 'author_id__in' => ['1', '2'], 'created__between' => ['2010-10-10', '2011-11-11']
end
def form(&block)
@@ -65,10 +65,10 @@ def form(&block)
it 'should be usable' do
doc = form do |f|
- f.select :"author_id[in]", [1,2,3,4,5]
+ f.select :"author_id__in", [1,2,3,4,5]
end
input = doc.find(:tag => "select")
- input['name'].should == "where[author_id[in]]"
+ input['name'].should == "where[author_id__in]"
choices = input.find_all(:tag => 'option')
choices.map {|n| n['value'] }.should =~ ['1', '2', '3', '4', '5']
choices.select {|n| n['selected'] }.map {|n| n['value'] }.should =~ ['1', '2']
View
8 spec/bsm/constrainable/model_spec.rb
@@ -29,7 +29,7 @@
end
it 'should delegate constrain to relation' do
- sql = Post.constrain(:author_id => {:in => 1}).to_sql
+ sql = Post.constrain(:author_id__in => 1).to_sql
sql.clean_sql.should == "SELECT posts.* FROM posts WHERE posts.author_id IN (1)"
end
@@ -38,9 +38,9 @@
Post.articles.constrain(nil).should have(1).record
Post.constrain(nil).articles.should have(1).record
- Post.articles.constrain(:author_id => {:in => 1}).should have(1).record
- Post.articles.constrain(:author_id => {:in => 2}).should have(:no).records
- Post.constrain(:author_id => {:in => 2}).should have(1).record
+ Post.articles.constrain(:author_id__in => 1).should have(1).record
+ Post.articles.constrain(:author_id__in => 2).should have(:no).records
+ Post.constrain(:author_id__in => 2).should have(1).record
end
it 'should retain association scopes' do
View
8 spec/bsm/constrainable/relation_spec.rb
@@ -11,21 +11,21 @@
end
it 'should apply constraints as parameters' do
- sql = relation.constrain(:author_id => {:in => [1, 2, 3]}, :created => { :gt => '2010-10-10' }).to_sql
+ sql = relation.constrain(:author_id__in => [1, 2, 3], :created__gt => '2010-10-10').to_sql
sql.clean_sql.should == "SELECT posts.* FROM posts WHERE posts.author_id IN (1, 2, 3) AND (posts.created_at > '2010-10-10 00:00:00')"
end
it 'should apply constraints as filter-sets' do
- filters = Post.constrainable.filter(:author_id => {:in => 1})
+ filters = Post.constrainable.filter(:author_id__in => 1)
sql = relation.constrain(filters).to_sql
sql.clean_sql.should == "SELECT posts.* FROM posts WHERE posts.author_id IN (1)"
end
it 'should respect constranable scopes' do
- sql = relation.constrain(:default, :author_id => {:in => 1}).to_sql
+ sql = relation.constrain(:default, :author_id__in => 1).to_sql
sql.clean_sql.should == "SELECT posts.* FROM posts WHERE posts.author_id IN (1)"
- sql = relation.constrain(:missing, :author_id => {:in => 1}).to_sql
+ sql = relation.constrain(:missing, :author_id__in => 1).to_sql
sql.clean_sql.should == "SELECT posts.* FROM posts"
end
View
4 spec/bsm/constrainable/schema_spec.rb
@@ -36,9 +36,9 @@
end
it 'should create filter-sets for given params' do
- fs = subject.filter 'author_id' => { 'in' => '1' }, 'created' => { 'gt' => '2010-10-10' }
+ fs = subject.filter 'author_id__in' => '1', 'created__gt' => '2010-10-10'
fs.should be_a(Bsm::Constrainable::FilterSet)
- fs.should == { "author_id"=>{:in=>"1"}, "created"=>{:gt=>"2010-10-10"} }
+ fs.should == { "author_id__in" => "1", "created__gt" => "2010-10-10" }
end
end

0 comments on commit 56a2941

Please sign in to comment.