outoftime-record_filter 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 1
4
- :patch: 1
4
+ :patch: 3
@@ -7,6 +7,8 @@ module RecordFilter
7
7
  end
8
8
 
9
9
  def named_filter(name, &block)
10
+ return if named_filters.include?(name.to_sym)
11
+ named_filters << name.to_sym
10
12
  DSL::DSL::subclass(self).module_eval do
11
13
  define_method(name, &block)
12
14
  end
@@ -17,6 +19,10 @@ module RecordFilter
17
19
  end
18
20
  end
19
21
  end
22
+
23
+ def named_filters
24
+ read_inheritable_attribute(:named_filters) || write_inheritable_attribute(:named_filters, [])
25
+ end
20
26
  end
21
27
  end
22
28
  end
@@ -7,6 +7,8 @@ module RecordFilter
7
7
  result = case dsl_conjunction.type
8
8
  when :any_of then AnyOf.new(table)
9
9
  when :all_of then AllOf.new(table)
10
+ when :none_of then NoneOf.new(table)
11
+ when :not_all_of then NotAllOf.new(table)
10
12
  end
11
13
 
12
14
  dsl_conjunction.steps.each do |step|
@@ -50,7 +52,13 @@ module RecordFilter
50
52
  end
51
53
 
52
54
  def add_join_on_association(association_name)
53
- @table.join_association(association_name)
55
+ table = @table
56
+ while association_name.is_a?(Hash)
57
+ result = table.join_association(association_name.keys[0])
58
+ table = result.right_table
59
+ association_name = association_name.values[0]
60
+ end
61
+ table.join_association(association_name)
54
62
  end
55
63
 
56
64
  def add_order(column_name, direction)
@@ -70,21 +78,25 @@ module RecordFilter
70
78
  end
71
79
 
72
80
  def to_conditions
73
- if @restrictions.empty?
74
- nil
75
- elsif @restrictions.length == 1
76
- @restrictions.first.to_conditions
77
- else
78
- @restrictions.map do |restriction|
79
- conditions = restriction.to_conditions
80
- conditions[0] = "(#{conditions[0]})"
81
- conditions
82
- end.inject do |conditions, new_conditions|
83
- conditions.first << " #{conjunctor} #{new_conditions.shift}"
84
- conditions.concat(new_conditions)
85
- conditions
81
+ result = begin
82
+ if @restrictions.empty?
83
+ nil
84
+ elsif @restrictions.length == 1
85
+ @restrictions.first.to_conditions
86
+ else
87
+ @restrictions.map do |restriction|
88
+ conditions = restriction.to_conditions
89
+ conditions[0] = "(#{conditions[0]})"
90
+ conditions
91
+ end.inject do |conditions, new_conditions|
92
+ conditions.first << " #{conjunctor} #{new_conditions.shift}"
93
+ conditions.concat(new_conditions)
94
+ conditions
95
+ end
86
96
  end
87
97
  end
98
+ result[0] = "!(#{result[0]})" if (negated && !result.nil? && !result[0].nil?)
99
+ result
88
100
  end
89
101
 
90
102
  protected
@@ -97,15 +109,23 @@ module RecordFilter
97
109
  end
98
110
 
99
111
  class AnyOf < Base
100
- def conjunctor
101
- 'OR'
102
- end
112
+ def conjunctor; 'OR'; end
113
+ def negated; false; end
103
114
  end
104
115
 
105
116
  class AllOf < Base
106
- def conjunctor
107
- 'AND'
108
- end
117
+ def conjunctor; 'AND'; end
118
+ def negated; false; end
119
+ end
120
+
121
+ class NoneOf < Base
122
+ def conjunctor; 'OR'; end
123
+ def negated; true; end
124
+ end
125
+
126
+ class NotAllOf < Base
127
+ def conjunctor; 'AND'; end
128
+ def negated; true; end
109
129
  end
110
130
  end
111
131
  end
@@ -30,6 +30,16 @@ module RecordFilter
30
30
  nil
31
31
  end
32
32
 
33
+ def none_of(&block)
34
+ @conjunction.add_conjunction(:none_of, &block)
35
+ nil
36
+ end
37
+
38
+ def not_all_of(&block)
39
+ @conjunction.add_conjunction(:not_all_of, &block)
40
+ nil
41
+ end
42
+
33
43
  # join
34
44
  def having(association, &block)
35
45
  join = @conjunction.add_join(association, &block)
@@ -0,0 +1,11 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class GroupBy
4
+ attr_reader :column
5
+
6
+ def initialize(column)
7
+ @column = column
8
+ end
9
+ end
10
+ end
11
+ end
@@ -8,7 +8,7 @@ module RecordFilter
8
8
  @column, @negated, @operator = column, negated, nil
9
9
  end
10
10
 
11
- [:equal_to, :is_null, :less_than, :less_than_or_equal_to, :greater_than, :greater_than_or_equal_to, :in, :between].each do |operator|
11
+ [:equal_to, :is_null, :less_than, :less_than_or_equal_to, :greater_than, :greater_than_or_equal_to, :in, :like].each do |operator|
12
12
  define_method(operator) do |*args|
13
13
  @value = args[0]
14
14
  @operator = operator
@@ -16,6 +16,16 @@ module RecordFilter
16
16
  end
17
17
  end
18
18
 
19
+ # Between can take either a tuple of [start, finish], a range, or two values.
20
+ def between(start, finish=nil)
21
+ @operator = :between
22
+ if !finish.nil?
23
+ @value = [start, finish]
24
+ else
25
+ @value = start
26
+ end
27
+ end
28
+
19
29
  alias_method :gt, :greater_than
20
30
  alias_method :gte, :greater_than_or_equal_to
21
31
  alias_method :lt, :less_than
@@ -17,7 +17,7 @@ module RecordFilter
17
17
  end
18
18
 
19
19
  def method_missing(method, *args, &block)
20
- if @clazz.respond_to?(method) # UGLY, we need to only pass through things that are named filters.
20
+ if @clazz.named_filters.include?(method)
21
21
  Filter.new(@clazz, method, @dsl.conjunction, *args)
22
22
  else
23
23
  loaded_data.send(method, *args, &block)
@@ -0,0 +1,23 @@
1
+ module RecordFilter
2
+ class GroupBy
3
+ attr_reader :column, :table
4
+
5
+ def initialize(column, table)
6
+ @column, @table = column, table
7
+ end
8
+
9
+ def to_sql
10
+ table, column = @table, @column
11
+ while column.is_a?(Hash)
12
+ table = table.join_association(column.keys[0]).right_table
13
+ column = column.values[0]
14
+ end
15
+
16
+ if (!table.has_column(column))
17
+ raise ColumnNotFoundException.new("The column #{column} was not found in #{table.table_name}.")
18
+ end
19
+
20
+ "#{table.table_name}.#{column}"
21
+ end
22
+ end
23
+ end
@@ -3,6 +3,7 @@ module RecordFilter
3
3
  class Base
4
4
  def initialize(column_name, value, options={})
5
5
  @column_name, @value, @negated = column_name, value, !!options.delete(:negated)
6
+ @value = @value.id if @value.kind_of?(ActiveRecord::Base)
6
7
  end
7
8
 
8
9
  def to_conditions
@@ -77,5 +78,15 @@ module RecordFilter
77
78
  ["#{@column_name} #{'NOT ' if @negated}BETWEEN ? AND ?", @value.first, @value.last]
78
79
  end
79
80
  end
81
+
82
+ class Like < Base
83
+ def to_positive_sql
84
+ "#{@column_name} LIKE ?"
85
+ end
86
+
87
+ def to_negative_sql
88
+ "#{@column_name} NOT LIKE ?"
89
+ end
90
+ end
80
91
  end
81
92
  end
@@ -0,0 +1,50 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'raising exceptions' do
4
+
5
+ describe 'on missing associations' do
6
+ it 'should get AssociationNotFoundException' do
7
+ lambda {
8
+ Post.filter do
9
+ having(:something_that_does_not_exist) do
10
+ with(:something_bad)
11
+ end
12
+ end.inspect
13
+ }.should raise_error(RecordFilter::AssociationNotFoundException)
14
+ end
15
+ end
16
+
17
+ describe 'on missing columns' do
18
+ it 'should get ColumnNotFoundException for with' do
19
+ lambda {
20
+ Post.filter do
21
+ with(:this_is_not_there, 2)
22
+ end.inspect
23
+ }.should raise_error(RecordFilter::ColumnNotFoundException)
24
+ end
25
+
26
+ it 'should get ColumnNotFoundException for without' do
27
+ lambda {
28
+ Post.filter do
29
+ without(:this_is_not_there, 2)
30
+ end.inspect
31
+ }.should raise_error(RecordFilter::ColumnNotFoundException)
32
+ end
33
+
34
+ it 'should get ColumnNotFoundException for order' do
35
+ lambda {
36
+ Post.filter do
37
+ order(:this_is_not_there, :asc)
38
+ end.inspect
39
+ }.should raise_error(RecordFilter::ColumnNotFoundException)
40
+ end
41
+
42
+ it 'should get AssociationNotFoundException for orders on bad associations' do
43
+ lambda {
44
+ Post.filter do
45
+ order({ :this_is_not_there => :eh }, :asc)
46
+ end.inspect
47
+ }.should raise_error(RecordFilter::AssociationNotFoundException)
48
+ end
49
+ end
50
+ end
@@ -93,6 +93,25 @@ describe 'implicit joins' do
93
93
  end
94
94
  end
95
95
 
96
+ describe 'with one having statement expressing multiple joins' do
97
+ before do
98
+ Blog.filter do
99
+ having(:posts => :comments) do
100
+ with :offensive, true
101
+ end
102
+ end.inspect
103
+ end
104
+
105
+ it 'should add both joins' do
106
+ Blog.last_find[:joins].should == %q(INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id ) +
107
+ %q(INNER JOIN "comments" AS blogs__posts__comments ON blogs__posts.id = blogs__posts__comments.post_id)
108
+ end
109
+
110
+ it 'should query against both conditions' do
111
+ Blog.last_find[:conditions].should == [%q(blogs__posts__comments.offensive = ?), true]
112
+ end
113
+ end
114
+
96
115
  describe 'on has_one' do
97
116
  before do
98
117
  Post.filter do
@@ -54,8 +54,22 @@ describe 'named filters' do
54
54
  end
55
55
  end
56
56
 
57
+ describe 'taking active_record objects as arguments' do
58
+ it 'should use the id of the object as the actual parameter' do
59
+ Post.named_filter(:with_ar_arg) do |blog|
60
+ with(:blog_id, blog)
61
+ end
62
+ blog = Blog.create
63
+ Post.with_ar_arg(blog).inspect
64
+ Post.last_find[:conditions].should == [%q("posts".blog_id = ?), blog.id]
65
+ end
66
+ end
67
+
57
68
  describe 'using filters in subclasses' do
58
69
  before do
70
+ Comment.named_filter(:with_contents) do |*args|
71
+ with :contents, args[0]
72
+ end
59
73
  class NiceComment < Comment
60
74
  extend TestModel
61
75
 
@@ -63,9 +77,6 @@ describe 'named filters' do
63
77
  with :offensive, true
64
78
  end
65
79
  end
66
- Comment.named_filter(:with_contents) do |*args|
67
- with :contents, args[0]
68
- end
69
80
  end
70
81
 
71
82
  it 'should execute the parent class filters correctly' do
@@ -81,17 +92,19 @@ describe 'named filters' do
81
92
  NiceComment.offensive.with_contents('something').inspect
82
93
  NiceComment.last_find[:conditions].should == [%q(("comments".offensive = ?) AND ("comments".contents = ?)), true, 'something']
83
94
  end
95
+
96
+ it 'should provide access to the named filters' do
97
+ Comment.named_filters.should == [:with_contents]
98
+ NiceComment.named_filters.sort_by { |i| i.to_s }.should == [:offensive, :with_contents]
99
+ end
84
100
  end
85
101
 
86
102
  describe 'using compound filters' do
87
- before do
103
+ it 'should concatenate the filters correctly' do
104
+ pending 'nested chaining'
88
105
  Post.named_filter(:with_offensive_comments) do
89
106
  having(:comments).offensive(true)
90
107
  end
91
- end
92
-
93
- it 'should concatenate the filters correctly' do
94
- pending 'nested chaining'
95
108
  Post.with_offensive_comments.inspect
96
109
  Post.last_find[:conditions].should == [%q(posts__comments.offensive = ?), true]
97
110
  Post.last_find[:joins].should == %q(INNER JOIN "comments" AS posts__comments ON "comments".post_id = posts__blog.id)
@@ -51,6 +51,40 @@ describe 'RecordFilter restrictions' do
51
51
  Post.last_find.should == { :conditions => [%q{"posts".created_at BETWEEN ? AND ?}, time1, time2] }
52
52
  end
53
53
 
54
+ it 'should filter for between with two arguments passed' do
55
+ Post.filter do
56
+ with(:id).between(1, 5)
57
+ end.inspect
58
+ Post.last_find.should == { :conditions => [%q("posts".id BETWEEN ? AND ?), 1, 5] }
59
+ end
60
+
61
+ it 'should filter for between with an array passed' do
62
+ Post.filter do
63
+ with(:id).between([2, 6])
64
+ end.inspect
65
+ Post.last_find.should == { :conditions => [%q("posts".id BETWEEN ? AND ?), 2, 6] }
66
+ end
67
+
68
+ it 'should filter by none_of' do
69
+ Post.filter do
70
+ none_of do
71
+ with(:blog_id, 1)
72
+ with(:permalink, 'eek')
73
+ end
74
+ end.inspect
75
+ Post.last_find.should == { :conditions => [%q{!(("posts".blog_id = ?) OR ("posts".permalink = ?))}, 1, 'eek'] }
76
+ end
77
+
78
+ it 'should filter by not_all_of' do
79
+ Post.filter do
80
+ not_all_of do
81
+ with(:blog_id, 1)
82
+ with(:permalink, 'eek')
83
+ end
84
+ end.inspect
85
+ Post.last_find.should == { :conditions => [%q{!(("posts".blog_id = ?) AND ("posts".permalink = ?))}, 1, 'eek'] }
86
+ end
87
+
54
88
  it 'should filter by disjunction' do
55
89
  Post.filter do
56
90
  any_of do
@@ -84,4 +118,18 @@ describe 'RecordFilter restrictions' do
84
118
  Post.last_find.should == { :conditions => [%q("posts".permalink IS NULL)] }
85
119
  end
86
120
  end
121
+
122
+ it 'should support like' do
123
+ Post.filter do
124
+ with(:permalink).like('%piglets%')
125
+ end.inspect
126
+ Post.last_find.should == { :conditions => [%q("posts".permalink LIKE ?), '%piglets%'] }
127
+ end
128
+
129
+ it 'should support NOT LIKE' do
130
+ Post.filter do
131
+ without(:permalink).like('%ostriches%')
132
+ end.inspect
133
+ Post.last_find.should == { :conditions => [%q("posts".permalink NOT LIKE ?), '%ostriches%'] }
134
+ end
87
135
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: outoftime-record_filter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat Brown
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-04-17 00:00:00 -07:00
13
+ date: 2009-04-20 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies: []
16
16
 
@@ -32,17 +32,20 @@ files:
32
32
  - lib/record_filter/dsl/conjunction.rb
33
33
  - lib/record_filter/dsl/conjunction_dsl.rb
34
34
  - lib/record_filter/dsl/dsl.rb
35
+ - lib/record_filter/dsl/group_by.rb
35
36
  - lib/record_filter/dsl/join.rb
36
37
  - lib/record_filter/dsl/limit.rb
37
38
  - lib/record_filter/dsl/order.rb
38
39
  - lib/record_filter/dsl/restriction.rb
39
40
  - lib/record_filter/filter.rb
41
+ - lib/record_filter/group_by.rb
40
42
  - lib/record_filter/join.rb
41
43
  - lib/record_filter/join_table.rb
42
44
  - lib/record_filter/order.rb
43
45
  - lib/record_filter/query.rb
44
46
  - lib/record_filter/restrictions.rb
45
47
  - lib/record_filter/table.rb
48
+ - spec/exception_spec.rb
46
49
  - spec/implicit_join_spec.rb
47
50
  - spec/limits_and_ordering_spec.rb
48
51
  - spec/models/blog.rb
@@ -81,6 +84,7 @@ signing_key:
81
84
  specification_version: 3
82
85
  summary: Pure-ruby criteria API for building complex queries in ActiveRecord
83
86
  test_files:
87
+ - spec/exception_spec.rb
84
88
  - spec/implicit_join_spec.rb
85
89
  - spec/limits_and_ordering_spec.rb
86
90
  - spec/models/blog.rb