aub-record_filter 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
+ :patch: 2
2
3
  :major: 0
3
4
  :minor: 1
4
- :patch: 1
data/lib/record_filter.rb CHANGED
@@ -2,9 +2,11 @@ require 'rubygems'
2
2
  gem 'activerecord', '~> 2.2'
3
3
  require 'active_record'
4
4
 
5
- %w(active_record query table conjunctions restrictions filter join order dsl).each do |file|
5
+ %w(active_record query table conjunctions restrictions filter join order group_by dsl).each do |file|
6
6
  require File.join(File.dirname(__FILE__), 'record_filter', file)
7
7
  end
8
8
 
9
9
  module RecordFilter
10
+ class AssociationNotFoundException < Exception; end
11
+ class ColumnNotFoundException < Exception; end
10
12
  end
@@ -5,8 +5,8 @@ module RecordFilter
5
5
 
6
6
  def self.create_from(dsl_conjunction, table)
7
7
  result = case dsl_conjunction.type
8
- when :any_of: AnyOf.new(table)
9
- when :all_of: AllOf.new(table)
8
+ when :any_of then AnyOf.new(table)
9
+ when :all_of then AllOf.new(table)
10
10
  end
11
11
 
12
12
  dsl_conjunction.steps.each do |step|
@@ -22,6 +22,8 @@ module RecordFilter
22
22
  result.add_limit_and_offset(step.limit, step.offset)
23
23
  when DSL::Order
24
24
  result.add_order(step.column, step.direction)
25
+ when DSL::GroupBy
26
+ result.add_group_by(step.column)
25
27
  end
26
28
  end
27
29
  result
@@ -35,6 +37,7 @@ module RecordFilter
35
37
  end
36
38
 
37
39
  def add_restriction(column_name, operator, value, options={})
40
+ check_column_exists!(column_name)
38
41
  restriction_class = "RecordFilter::Restrictions::#{operator.to_s.camelize}".constantize
39
42
  restriction = restriction_class.new("#{@table_name}.#{column_name}", value, options)
40
43
  self << restriction
@@ -50,8 +53,12 @@ module RecordFilter
50
53
  @table.join_association(association_name)
51
54
  end
52
55
 
53
- def add_order(column, direction)
54
- @table.order_column(column, direction)
56
+ def add_order(column_name, direction)
57
+ @table.order_column(column_name, direction)
58
+ end
59
+
60
+ def add_group_by(column_name)
61
+ @table.group_by_column(column_name)
55
62
  end
56
63
 
57
64
  def add_limit_and_offset(limit, offset)
@@ -79,6 +86,14 @@ module RecordFilter
79
86
  end
80
87
  end
81
88
  end
89
+
90
+ protected
91
+
92
+ def check_column_exists!(column_name)
93
+ if (!@table.has_column(column_name))
94
+ raise ColumnNotFoundException.new("The column #{column_name} was not found in #{@table.table_name}.")
95
+ end
96
+ end
82
97
  end
83
98
 
84
99
  class AnyOf < Base
@@ -1,4 +1,4 @@
1
- %w(conjunction conjunction_dsl dsl join limit order restriction).each { |file| require File.join(File.dirname(__FILE__), 'dsl', file) }
1
+ %w(conjunction conjunction_dsl dsl group_by join limit order restriction).each { |file| require File.join(File.dirname(__FILE__), 'dsl', file) }
2
2
 
3
3
  module RecordFilter
4
4
  module DSL
@@ -42,6 +42,10 @@ module RecordFilter
42
42
  def add_order(column, direction)
43
43
  @steps << Order.new(column, direction)
44
44
  end
45
+
46
+ def add_group_by(column)
47
+ @steps << GroupBy.new(column)
48
+ end
45
49
  end
46
50
  end
47
51
  end
@@ -37,6 +37,11 @@ module RecordFilter
37
37
  @conjunction.add_order(column, direction)
38
38
  nil
39
39
  end
40
+
41
+ def group_by(column)
42
+ @conjunction.add_group_by(column)
43
+ nil
44
+ end
40
45
  end
41
46
  end
42
47
  end
@@ -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
@@ -20,6 +20,8 @@ module RecordFilter
20
20
  alias_method :gte, :greater_than_or_equal_to
21
21
  alias_method :lt, :less_than
22
22
  alias_method :lte, :less_than_or_equal_to
23
+ alias_method :null, :is_null
24
+ alias_method :nil, :is_null
23
25
  end
24
26
  end
25
27
  end
@@ -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
@@ -18,6 +18,10 @@ module RecordFilter
18
18
  column = column.values[0]
19
19
  end
20
20
 
21
+ if (!table.has_column(column))
22
+ raise ColumnNotFoundException.new("The column #{column} was not found in #{table.table_name}.")
23
+ end
24
+
21
25
  "#{table.table_name}.#{column} #{dir}"
22
26
  end
23
27
  end
@@ -10,8 +10,10 @@ module RecordFilter
10
10
  params = { :conditions => @conjunction.to_conditions }
11
11
  joins = @table.all_joins
12
12
  params[:joins] = joins.map { |join| join.to_sql } * ' ' unless joins.empty?
13
- orders = @table.all_orders
13
+ orders = @table.orders
14
14
  params[:order] = orders.map { |order| order.to_sql } * ', ' unless orders.empty?
15
+ group_bys = @table.group_bys
16
+ params[:group] = group_bys.map { |group_by| group_by.to_sql } * ', ' unless group_bys.empty?
15
17
  params[:limit] = @conjunction.limit if @conjunction.limit
16
18
  params[:offset] = @conjunction.offset if @conjunction.offset
17
19
  params
@@ -1,6 +1,6 @@
1
1
  module RecordFilter
2
2
  class Table
3
- attr_reader :table_alias
3
+ attr_reader :table_alias, :orders, :group_bys
4
4
 
5
5
  def initialize(model_class, table_alias = nil)
6
6
  @model_class = model_class
@@ -9,6 +9,7 @@ module RecordFilter
9
9
  @joins_cache = {}
10
10
  @joins = []
11
11
  @orders = []
12
+ @group_bys = []
12
13
  end
13
14
 
14
15
  def table_name
@@ -19,6 +20,9 @@ module RecordFilter
19
20
  @joins_cache[association_name] ||=
20
21
  begin
21
22
  association = @model_class.reflect_on_association(association_name)
23
+ if association.nil?
24
+ raise AssociationNotFoundException.new("The association #{association_name} was not found on #{@model_class.name}.")
25
+ end
22
26
  case association.macro
23
27
  when :belongs_to, :has_many, :has_one
24
28
  simple_join(association)
@@ -39,11 +43,12 @@ module RecordFilter
39
43
  @orders << Order.new(column, direction, self)
40
44
  end
41
45
 
42
- def all_orders
43
- @orders + @joins.inject([]) do |child_orders, join|
44
- child_orders.concat(join.right_table.all_orders)
45
- child_orders
46
- end
46
+ def group_by_column(column)
47
+ @group_bys << GroupBy.new(column, self)
48
+ end
49
+
50
+ def has_column(column_name)
51
+ @model_class.column_names.include?(column_name.to_s)
47
52
  end
48
53
 
49
54
  private
@@ -88,6 +93,7 @@ module RecordFilter
88
93
  @joins_cache = {}
89
94
  @joins = []
90
95
  @orders = []
96
+ @group_bys = []
91
97
  end
92
98
  end
93
99
  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
@@ -9,7 +9,7 @@ describe 'implicit joins' do
9
9
  describe 'with single condition inline' do
10
10
  before do
11
11
  Post.filter do
12
- having(:blog).with :title, 'Test Title'
12
+ having(:blog).with :name, 'Test Name'
13
13
  end.inspect
14
14
  end
15
15
 
@@ -18,7 +18,7 @@ describe 'implicit joins' do
18
18
  end
19
19
 
20
20
  it 'should query against condition on join table' do
21
- Post.last_find[:conditions].should == ['posts__blog.title = ?', 'Test Title']
21
+ Post.last_find[:conditions].should == ['posts__blog.name = ?', 'Test Name']
22
22
  end
23
23
  end
24
24
 
@@ -28,14 +28,14 @@ describe 'implicit joins' do
28
28
  end
29
29
 
30
30
  it 'should query against conditions on join table' do
31
- Post.last_find[:conditions].should == [%q((posts__blog.title = ?) AND (posts__blog.published = ?)), 'Test Title', true]
31
+ Post.last_find[:conditions].should == [%q((posts__blog.name = ?) AND (posts__blog.published = ?)), 'Test Name', true]
32
32
  end
33
33
  end
34
34
 
35
35
  describe 'with multiple conditions on single join inline' do
36
36
  before do
37
37
  Post.filter do
38
- having(:blog).with :title, 'Test Title'
38
+ having(:blog).with :name, 'Test Name'
39
39
  having(:blog).with :published, true
40
40
  end.inspect
41
41
  end
@@ -47,7 +47,7 @@ describe 'implicit joins' do
47
47
  before do
48
48
  Post.filter do
49
49
  having :blog do
50
- with :title, 'Test Title'
50
+ with :name, 'Test Name'
51
51
  with :published, true
52
52
  end
53
53
  end.inspect
@@ -154,13 +154,13 @@ describe 'implicit joins' do
154
154
  describe 'with nil conditions' do
155
155
  before do
156
156
  Comment.filter do
157
- with :content, nil
157
+ with :contents, nil
158
158
  with :offensive, true
159
159
  end.inspect
160
160
  end
161
161
 
162
162
  it 'should create the correct IS NULL condition' do
163
- Comment.last_find[:conditions].should == [%q(("comments".content IS NULL) AND ("comments".offensive = ?)), true]
163
+ Comment.last_find[:conditions].should == [%q(("comments".contents IS NULL) AND ("comments".offensive = ?)), true]
164
164
  end
165
165
  end
166
166
 
@@ -179,13 +179,13 @@ describe 'implicit joins' do
179
179
  describe 'with negated nil conditions' do
180
180
  before do
181
181
  Comment.filter do
182
- without :content, nil
182
+ without :contents, nil
183
183
  with :offensive, true
184
184
  end.inspect
185
185
  end
186
186
 
187
187
  it 'should create the correct IS NOT NULL condition' do
188
- Comment.last_find[:conditions].should == [%q(("comments".content IS NOT NULL) AND ("comments".offensive = ?)), true]
188
+ Comment.last_find[:conditions].should == [%q(("comments".contents IS NOT NULL) AND ("comments".offensive = ?)), true]
189
189
  end
190
190
  end
191
191
  end
@@ -161,4 +161,30 @@ describe 'filter qualifiers' do
161
161
  end
162
162
  end
163
163
  end
164
+
165
+ describe 'group_by' do
166
+ it 'should add the group for a simple column' do
167
+ Post.filter do
168
+ group_by(:created_at)
169
+ end.inspect
170
+ Post.last_find[:group].should == %q("posts".created_at)
171
+ end
172
+
173
+ it 'should add the group for multiple column' do
174
+ Post.filter do
175
+ group_by(:created_at)
176
+ group_by(:published)
177
+ end.inspect
178
+ Post.last_find[:group].should == %q("posts".created_at, "posts".published)
179
+ end
180
+
181
+ it 'should add the group for joined columns' do
182
+ Post.filter do
183
+ having(:photo)
184
+ group_by(:created_at)
185
+ group_by(:photo => :format)
186
+ end.inspect
187
+ Post.last_find[:group].should == %q("posts".created_at, "photos".format)
188
+ end
189
+ end
164
190
  end
@@ -75,4 +75,13 @@ describe 'RecordFilter restrictions' do
75
75
  Post.last_find.should == { :conditions => [%q{(("posts".blog_id = ?) AND ("posts".permalink = ?)) OR ("posts".permalink = ?)},
76
76
  1, 'my-post', 'another-post'] }
77
77
  end
78
+
79
+ it 'should filter for nil' do
80
+ [:is_null, :null, :nil].each do |method|
81
+ Post.filter do
82
+ with(:permalink).send(method)
83
+ end.inspect
84
+ Post.last_find.should == { :conditions => [%q("posts".permalink IS NULL)] }
85
+ end
86
+ end
78
87
  end
data/spec/test.db CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aub-record_filter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat Brown
@@ -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