aub-record_filter 0.1.1 → 0.1.2

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
+ :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