Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge pull request #1 from dolzenko/master

Empty string fields are ignore by default, add Boolean field type
  • Loading branch information...
commit 521fce0ce1585547608eba365e7251bd8128c7c8 2 parents 438fac2 + fc14200
Ev Dolzhenko dolzenko authored
10 README.markdown
Source Rendered
... ... @@ -1,10 +1,10 @@
1 1 # Constrainable
2 2
3   -Simple filtering for ActiveRecord. Sanitizes simple and readable query parameters -great for building APIs & HTML filters.
  3 +Simple filtering for ActiveRecord. Sanitizes simple and readable query parameters - great for building APIs & HTML filters.
4 4
5 5 ## Straight to the point. Examples:
6 6
7   -Let's asume we have a model called Post, defined as:
  7 +Let's assume we have a model called Post, defined as:
8 8 Post(id: integer, title: string, body: string, author_id: integer, category: string, created_at: datetime, updated_at: datetime)
9 9
10 10 In the simplest possible case you can define a few attributes and start filtering:
@@ -17,7 +17,7 @@ In the simplest possible case you can define a few attributes and start filterin
17 17
18 18 end
19 19
20   - # Examle request:
  20 + # Example request:
21 21 # GET /posts?where[id__not_eq]=1&where[author_id__eq]=2
22 22 # Params:
23 23 # "where" => { "id__not_eq" => "1", "author_id__eq" => "2" }
@@ -84,8 +84,8 @@ Integration with controllers, views & filter forms:
84 84 respond_to :html
85 85
86 86 def index
87   - @filters = Post.constrainable.fliter(params[:where])
88   - @posts = Post.constrain(@filters)
  87 + @filters = Post.constrainable.filter(params[:where])
  88 + @posts = Post.constrain(@filters)
89 89 respond_with @posts
90 90 end
91 91 end
2  lib/bsm/constrainable/field.rb
@@ -9,6 +9,7 @@ module Bsm::Constrainable::Field
9 9 autoload :Timestamp,'bsm/constrainable/field/common'
10 10 autoload :Datetime, 'bsm/constrainable/field/common'
11 11 autoload :Date, 'bsm/constrainable/field/common'
  12 + autoload :Boolean, 'bsm/constrainable/field/common'
12 13
13 14 register self::Number
14 15 register self::Integer
@@ -17,4 +18,5 @@ module Bsm::Constrainable::Field
17 18 register self::Timestamp
18 19 register self::Datetime
19 20 register self::Date
  21 + register self::Boolean
20 22 end
11 lib/bsm/constrainable/field/base.rb
@@ -11,12 +11,12 @@ def self.kind
11 11 @kind ||= name.demodulize.underscore.to_sym
12 12 end
13 13
14   - attr_reader :name, :operators, :attribute, :scope
  14 + attr_reader :name, :operators, :attribute, :scope, :options
15 15
16 16 # Accepts a name and options. Valid options are:
17 17 # * <tt>:using</tt> - a Symbol or a Proc pointing to a DB column, optional (uses name by default)
18 18 # * <tt>:with</tt> - a list of operators to use
19   - # * <tt>:scope</tt> - a Proc containing additonal scopes
  19 + # * <tt>:scope</tt> - a Proc containing additional scopes
20 20 #
21 21 # Examples:
22 22 #
@@ -28,10 +28,11 @@ def self.kind
28 28 #
29 29 def initialize(name, options = {})
30 30 @name = name.to_s
31   - @attribute = options[:using] || name
32   - @operators = Set.new(self.class.operators & Array.wrap(options[:with]))
  31 + @options = options.dup
  32 + @attribute = @options.delete(:using) || name
  33 + @operators = Set.new(self.class.operators & Array.wrap(@options.delete(:with)))
33 34 @operators = Set.new(self.class.defaults) if @operators.empty?
34   - @scope = options[:scope]
  35 + @scope = @options.delete(:scope)
35 36 end
36 37
37 38 # Merge params into a relation
19 lib/bsm/constrainable/field/common.rb
@@ -23,6 +23,11 @@ class Decimal < Number
23 23
24 24 class String < Base
25 25 self.operators = [:eq, :not_eq]
  26 +
  27 + def _convert(v)
  28 + result = super
  29 + result.blank? && !options[:allow_blank] ? nil : result
  30 + end
26 31 end
27 32
28 33 class Timestamp < Base
@@ -43,4 +48,18 @@ def _convert(v)
43 48 v.to_date rescue nil
44 49 end
45 50 end
  51 +
  52 + class Boolean < Base
  53 + self.operators = [:eq, :not_eq]
  54 +
  55 + TRUE_VALUES = ["true", "1"]
  56 +
  57 + protected
  58 +
  59 + def _convert(v)
  60 + result = super
  61 + result.blank? ? nil : TRUE_VALUES.include?(result)
  62 + end
  63 + end
  64 +
46 65 end
2  lib/bsm/constrainable/relation.rb
... ... @@ -1,7 +1,7 @@
1 1 # Extension for ActiveRecord::Relation
2 2 module Bsm::Constrainable::Relation
3 3
4   - # Apply contraints. Example:
  4 + # Apply constraints. Example:
5 5 #
6 6 # Post.constrain("created_at__lt" => "2011-01-01")
7 7 #
2  lib/bsm/constrainable/schema.rb
@@ -40,7 +40,7 @@ def fields(*names)
40 40 # # Complex example, using an attribute from another table, and ensure it's included (LEFT OUTER JOIN)
41 41 # match :author, :using => proc { Author.scope.table[:name] }, :scope => { includes(:author) }, :as => :string, :with => [:eq, :matches]
42 42 #
43   - # There are also several short-cutrs available. Examples:
  43 + # There are also several short-cuts available. Examples:
44 44 #
45 45 # timestamp :created, :using => :created_at, :with => [:lt, :between]
46 46 # number :id, :author_id
4 spec/bsm/constrainable/field/base_spec.rb
@@ -63,5 +63,9 @@ def merge(name, operator, value)
63 63 rel.first.should == posts(:article)
64 64 end
65 65
  66 + it 'should store unrecognized options' do
  67 + Bsm::Constrainable::Field::String.new("some", :allow_blank => true).options.should == { :allow_blank => true }
  68 + end
  69 +
66 70 end
67 71
19 spec/bsm/constrainable/field/common_spec.rb
@@ -2,9 +2,7 @@
2 2
3 3 describe "Common Fields" do
4 4
5   - def subject(value = "")
6   - described_class.new(value)
7   - end
  5 + let(:subject) { described_class.new('') }
8 6
9 7 describe Bsm::Constrainable::Field::Number do
10 8 it { subject.class.should have(9).operators }
@@ -32,6 +30,11 @@ def subject(value = "")
32 30 it { subject.class.should have(2).operators }
33 31 it { subject.convert(1).should == "1" }
34 32 it { subject.convert(["a", 1]).should == ["a", "1"] }
  33 + it { subject.convert("").should be_nil }
  34 + it {
  35 + subject.options[:allow_blank] = true
  36 + subject.convert("").should == ""
  37 + }
35 38 end
36 39
37 40 describe Bsm::Constrainable::Field::Timestamp do
@@ -52,5 +55,15 @@ def subject(value = "")
52 55 it { subject.convert("2011-11-11 11:11").should == Date.civil(2011, 11, 11) }
53 56 end
54 57
  58 + describe Bsm::Constrainable::Field::Boolean do
  59 + it { subject.class.should have(2).operators }
  60 + it { subject.convert("true").should == true }
  61 + it { subject.convert("1").should == true }
  62 + it { subject.convert("false").should == false }
  63 + it { subject.convert("0").should == false }
  64 + it { subject.convert("").should == nil }
  65 + it { subject.convert("invalid").should == nil }
  66 + end
  67 +
55 68 end
56 69

0 comments on commit 521fce0

Please sign in to comment.
Something went wrong with that request. Please try again.