record_filter 0.9.12

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.
Files changed (53) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG +232 -0
  3. data/README.rdoc +354 -0
  4. data/Rakefile +92 -0
  5. data/TODO +3 -0
  6. data/VERSION.yml +4 -0
  7. data/config/roodi.yml +14 -0
  8. data/lib/record_filter/active_record.rb +108 -0
  9. data/lib/record_filter/column_parser.rb +14 -0
  10. data/lib/record_filter/conjunctions.rb +169 -0
  11. data/lib/record_filter/dsl/class_join.rb +16 -0
  12. data/lib/record_filter/dsl/conjunction.rb +57 -0
  13. data/lib/record_filter/dsl/conjunction_dsl.rb +317 -0
  14. data/lib/record_filter/dsl/dsl.rb +143 -0
  15. data/lib/record_filter/dsl/dsl_factory.rb +19 -0
  16. data/lib/record_filter/dsl/group_by.rb +11 -0
  17. data/lib/record_filter/dsl/join.rb +12 -0
  18. data/lib/record_filter/dsl/join_condition.rb +21 -0
  19. data/lib/record_filter/dsl/join_dsl.rb +49 -0
  20. data/lib/record_filter/dsl/limit.rb +12 -0
  21. data/lib/record_filter/dsl/named_filter.rb +12 -0
  22. data/lib/record_filter/dsl/order.rb +12 -0
  23. data/lib/record_filter/dsl/restriction.rb +314 -0
  24. data/lib/record_filter/dsl.rb +21 -0
  25. data/lib/record_filter/filter.rb +105 -0
  26. data/lib/record_filter/group_by.rb +21 -0
  27. data/lib/record_filter/join.rb +66 -0
  28. data/lib/record_filter/order.rb +27 -0
  29. data/lib/record_filter/query.rb +60 -0
  30. data/lib/record_filter/restriction_factory.rb +21 -0
  31. data/lib/record_filter/restrictions.rb +97 -0
  32. data/lib/record_filter/table.rb +172 -0
  33. data/lib/record_filter.rb +35 -0
  34. data/record_filter.gemspec +108 -0
  35. data/script/console +8 -0
  36. data/spec/active_record_spec.rb +211 -0
  37. data/spec/exception_spec.rb +208 -0
  38. data/spec/explicit_join_spec.rb +132 -0
  39. data/spec/implicit_join_spec.rb +403 -0
  40. data/spec/limits_and_ordering_spec.rb +230 -0
  41. data/spec/models.rb +109 -0
  42. data/spec/named_filter_spec.rb +264 -0
  43. data/spec/proxying_spec.rb +63 -0
  44. data/spec/restrictions_spec.rb +251 -0
  45. data/spec/select_spec.rb +79 -0
  46. data/spec/spec_helper.rb +39 -0
  47. data/spec/test.db +0 -0
  48. data/tasks/db.rake +106 -0
  49. data/tasks/rcov.rake +9 -0
  50. data/tasks/spec.rake +10 -0
  51. data/test/performance_test.rb +39 -0
  52. data/test/test.db +0 -0
  53. metadata +137 -0
@@ -0,0 +1,317 @@
1
+ module RecordFilter
2
+ module DSL
3
+ # The ConjunctionDSL is used for specifying restrictions, conjunctions and joins, with methods that
4
+ # can be accessed from any point in a filter declaration. The where method is used for creating
5
+ # restrictions, conjunctions are specified through any_of, all_of, none_of and not_all_of, and joins
6
+ # are described by having and join.
7
+ class ConjunctionDSL
8
+
9
+ attr_reader :conjunction # :nodoc:
10
+
11
+ def initialize(model_class, conjunction) # :nodoc:
12
+ @model_class = model_class
13
+ @conjunction = conjunction
14
+ end
15
+
16
+ # Specify a condition on the given column, which will be added to the WHERE clause
17
+ # of the resulting query. This method returns a Restriction object, which can be called
18
+ # with any of the specific restriction methods described there in order to create many
19
+ # types of conditions. If both a column name and a value are passed, this will automatically
20
+ # create an equality condition, so the following two examples are equal:
21
+ # with(:permalink, 'junk')
22
+ # with(:permalink).equal_to('junk')
23
+ # If nil is passed as the second argument, an is_null restriction will automatically be
24
+ # created, so these two examples are equal as well:
25
+ # with(:permalink, nil)
26
+ # with(:permalink).is_null
27
+ # This method can be called at any point in the filter specification, and the appropriate
28
+ # clauses will be created if it is called within or other conjunctions.
29
+ #
30
+ # ==== Parameters
31
+ # column<Symbol>::
32
+ # The name of the column to restrict. The column is assumed to exist in the table that is
33
+ # currently in scope. In the outer block of a filter, that would be the table being filtered,
34
+ # and within joins it would be the table being joined.
35
+ # value<value, optional>::
36
+ # If specified, the value will be used to automatically create either an equality restriction
37
+ # or an IS NULL test, as described above.
38
+ #
39
+ # ==== Returns
40
+ # Restriction::
41
+ # A restriction object that can be used to create a specific condition. See the API in
42
+ # Restriction for options.
43
+ #
44
+ # ==== Alternatives
45
+ # The value parameter is optional, as described above.
46
+ #
47
+ # @public
48
+ def with(column, value=Restriction::DEFAULT_VALUE)
49
+ return @conjunction.add_restriction(column, value)
50
+ end
51
+
52
+ # Add a where clause that will pass if any of the conditions specified within it
53
+ # are true. Any restrictions created inside the given block are OR'ed together
54
+ # in the final query, and the block can contain any number of joins, restrictions, or
55
+ # other conjunctions.
56
+ # Blog.filter do
57
+ # any_of do
58
+ # with(:created_at, nil)
59
+ # with(:created_at).greater_than(3.days.ago)
60
+ # end
61
+ # end
62
+ #
63
+ # # :conditions => { ['blogs.created_at IS NULL OR blogs.created_at > ?', 3.days.ago] }
64
+ #
65
+ # ==== Parameters
66
+ # block<Proc>::
67
+ # The block can contain any sequence of calls, and the conditions that it contains will be
68
+ # OR'ed together to create a where clause.
69
+ #
70
+ # ==== Returns
71
+ # nil
72
+ #
73
+ # @public
74
+ def any_of(&block)
75
+ @conjunction.add_conjunction(:any_of, &block)
76
+ nil
77
+ end
78
+
79
+ # Add a where clause that will pass only if all of the conditions specified within it
80
+ # are true. Any restrictions created inside the given block are AND'ed together
81
+ # in the final query, and the block can contain any number of joins, restrictions, or
82
+ # other conjunctions.
83
+ # Blog.filter do
84
+ # all_of do
85
+ # with(:created_at, nil)
86
+ # with(:created_at).greater_than(3.days.ago)
87
+ # end
88
+ # end
89
+ #
90
+ # # :conditions => { ['blogs.created_at IS NULL AND blogs.created_at > ?', 3.days.ago] }
91
+ #
92
+ # ==== Parameters
93
+ # block<Proc>::
94
+ # The block can contain any sequence of calls, and the conditions that it contains will be
95
+ # AND'ed together to create a where clause.
96
+ #
97
+ # ==== Returns
98
+ # nil
99
+ #
100
+ # @public
101
+ def all_of(&block)
102
+ @conjunction.add_conjunction(:all_of, &block)
103
+ nil
104
+ end
105
+
106
+ # Add a where clause that will pass only if none of the conditions specified within it
107
+ # are true. Any restrictions created inside the given block are OR'ed together
108
+ # in the final query and the result is negated. The block can contain any number of joins,
109
+ # restrictions, or other conjunctions.
110
+ # Blog.filter do
111
+ # none_of do
112
+ # with(:created_at, nil)
113
+ # with(:created_at).greater_than(3.days.ago)
114
+ # end
115
+ # end
116
+ #
117
+ # # :conditions => { ['NOT (blogs.created_at IS NULL OR blogs.created_at > ?)', 3.days.ago] }
118
+ #
119
+ # ==== Parameters
120
+ # block<Proc>::
121
+ # The block can contain any sequence of calls, and the conditions that it contains will be
122
+ # OR'ed together and then negated to create a where clause.
123
+ #
124
+ # ==== Returns
125
+ # nil
126
+ #
127
+ # @public
128
+ def none_of(&block)
129
+ @conjunction.add_conjunction(:none_of, &block)
130
+ nil
131
+ end
132
+
133
+ # Add a where clause that will pass unless all of the conditions specified within it
134
+ # are true. Any restrictions created inside the given block are AND'ed together
135
+ # in the final query and the result is negated. The block can contain any number of joins,
136
+ # restrictions, or other conjunctions.
137
+ # Blog.filter do
138
+ # none_of do
139
+ # with(:created_at, nil)
140
+ # with(:created_at).greater_than(3.days.ago)
141
+ # end
142
+ # end
143
+ #
144
+ # # :conditions => { ['NOT (blogs.created_at IS NULL AND blogs.created_at > ?)', 3.days.ago] }
145
+ #
146
+ # ==== Parameters
147
+ # block<Proc>::
148
+ # The block can contain any sequence of calls, and the conditions that it contains will be
149
+ # AND'ed together and then negated to create a where clause.
150
+ #
151
+ # ==== Returns
152
+ # nil
153
+ #
154
+ # @public
155
+ def not_all_of(&block)
156
+ @conjunction.add_conjunction(:not_all_of, &block)
157
+ nil
158
+ end
159
+
160
+ # Create an implicit join using an association as the target. This method allows you to
161
+ # easily specify a join without specifying the columns to use by taking any needed data
162
+ # from the given ActiveRecord association. If provided, the block will be evaluated in
163
+ # the context of the table that has been joined, so any restrictions or other joins will
164
+ # be performed using its columns and associations. For example, if a Post has_many comments
165
+ # then the following code will join to the comments table and restrict the comments based
166
+ # on their created_at field:
167
+ # Post.filter do
168
+ # having(:comments) do
169
+ # with(:created_at).greater_than(3.days.ago)
170
+ # end
171
+ # end
172
+ # Options can be passed to provide a custom join type or table alias through the options
173
+ # hash. The :join_type option can be either :inner, :left or :right and will default to
174
+ # :inner if not provided. The :alias option allows you to provide an alias for use in joining
175
+ # the table. If the same association is joined twice with different aliases, it will be treated
176
+ # as two separate joins. By default an alias will automatically be created
177
+ # for the joined table named "#{left_table}__#{association_name}", so in the above example, the
178
+ # alias would be posts__comments. It is also possible to provide a hash as the association
179
+ # name, in which case a trail of associations can be joined in one statment.
180
+ #
181
+ # ==== Parameters
182
+ # association<Symbol>::
183
+ # The name of the association to use as a base for the join.
184
+ # options<Hash>::
185
+ # An options hash (see below)
186
+ #
187
+ # ==== Options (options)
188
+ # :join_type<Symbol>::
189
+ # The type of join to use. Available options are :inner, :left and :right. Defaults to :inner.
190
+ # :alias<String>::
191
+ # An alias to use for the table name in the join. If provided, will create a unique name for
192
+ # the join and allow the same association to be joined multiple times. By default, the alias
193
+ # will be "#{left_table}__#{association_name}".
194
+ #
195
+ # ==== Returns
196
+ # ConjunctionDSL::
197
+ # A DSL object is returned in order to allow constructs like: having(:comments).with(:offensive, true)
198
+ #
199
+ # ==== Alternatives
200
+ # If only one argument is given, the join type will default to :inner and the first argument will
201
+ # be used as the association name.
202
+ #
203
+ # @public
204
+ def having(association, options={}, &block)
205
+ @conjunction.add_join(association, options[:join_type], options[:alias], &block)
206
+ end
207
+
208
+ # Create an explicit join on the table of the given class. This method allows more complex
209
+ # joins to be speficied than can be created using having, including jump joins and ones that
210
+ # include conditions on column values. The method accepts a block that can contain any sequence
211
+ # of conjunctions, restrictions, or other joins, but it must also contain at least one call to
212
+ # JoinDSL.on to specify the conditions for the join. The options hash accepts :join_type and
213
+ # :alias parameters. The :join_type parameter can be either :inner, :left or :right and defaults
214
+ # to :inner. The :alias parameter allows you to specify an alias for the table in the join and
215
+ # defaults to "#{left_table}__#{clazz.name}".
216
+ #
217
+ # ==== Parameters
218
+ # clazz<Class>::
219
+ # The class that is being joined to.
220
+ # options<Hash>::
221
+ # An options hash (see below)
222
+ # block<Proc>
223
+ # The contents of the join block can contain any sequence of conjunctions, restrictions, or joins.
224
+ #
225
+ # ==== Options(options)
226
+ # :join_type<Symbol>::
227
+ # Indicates the type of join to use and must be one of :inner, :left or :right, where :left
228
+ # or :right will create a LEFT or RIGHT OUTER join respectively.
229
+ # :alias<String>::
230
+ # If provided, will specify an alias to use in the SQL when referring to the joined table.
231
+ # If the argument is not given, the alias will be "#{left_table}__#{clazz.name}"
232
+ #
233
+ # ==== Returns
234
+ # JoinDSL::
235
+ # A DSL object that can be used to specify the contents of the join. Returning this value allows
236
+ # for constructions like: join(Comment, :inner).on(:id => :post_id)
237
+ #
238
+ # @public
239
+ def join(clazz, options={}, &block)
240
+ @conjunction.add_class_join(clazz, options[:join_type], options[:alias], &block)
241
+ end
242
+
243
+ # Access the class that the current filter is being applied to. This is necessary
244
+ # because the filter is evaluated in the context of the DSL object, so self will
245
+ # not give access to any methods that need to be called on the filtered class.
246
+ # It is especially useful in named filters that may be defined in a way that allows
247
+ # them to apply to multiple classes.
248
+ #
249
+ # ==== Returns
250
+ # Class::
251
+ # The class that is currently being filtered.
252
+ #
253
+ # @public
254
+ def filter_class
255
+ @model_class
256
+ end
257
+
258
+ # Enable calling of named filters from within other filters by catching unknown calls
259
+ # and assuming that they are to named filters. This enables the following examples:
260
+ # class Post < ActiveRecord::Base
261
+ # has_many :comments
262
+ # named_filter(:empty) { with(:contents).nil }
263
+ # end
264
+ #
265
+ # class Comment < ActiveRecord::Base
266
+ # belongs_to :post
267
+ # named_filter(:offensive) { |value| with(:offensive, value) }
268
+ # end
269
+ #
270
+ # Post.filter do
271
+ # with(:created_at).less_than(1.hour.ago)
272
+ # empty
273
+ # end
274
+ #
275
+ # # Results in:
276
+ # # :conditions => { ['posts.created_at < ? AND posts.contents IS NULL', 1.hour.ago] }
277
+ # # And even cooler:
278
+ #
279
+ # Post.filter do
280
+ # having(:comments).offensive(true)
281
+ # end
282
+ #
283
+ # # Results in:
284
+ # # :conditions => { ['posts__comments.offensive = ?', true] }
285
+ # # :joins => { 'INNER JOIN "comments" AS posts__comments ON "posts".id = posts__comments.post_id' }
286
+ #
287
+ # ==== Parameters
288
+ # args<Array>::
289
+ # The arguments to pass to the named filter when called.
290
+ #
291
+ # @public
292
+ def method_missing(method, *args)
293
+ @conjunction.add_named_filter(method, *args)
294
+ end
295
+
296
+ #
297
+ # Define these_methods here just so that we can throw exceptions when they are called. They should not
298
+ # be callable in the scope of a conjunction_dsl.
299
+ #
300
+ def limit(limit, offset=nil) # :nodoc:
301
+ raise InvalidFilterException.new('Calls to limit can only be made in the outer block of a filter.')
302
+ end
303
+
304
+ def order(column, direction=:asc) # :nodoc:
305
+ raise InvalidFilterException.new('Calls to order can only be made in the outer block of a filter.')
306
+ end
307
+
308
+ def group_by(column) # :nodoc:
309
+ raise InvalidFilterException.new('Calls to group_by can only be made in the outer block of a filter.')
310
+ end
311
+
312
+ def on(column, value=Restriction::DEFAULT_VALUE) # :nodoc:
313
+ raise InvalidFilterException.new('Calls to on can only be made in the block of a call to join.')
314
+ end
315
+ end
316
+ end
317
+ end
@@ -0,0 +1,143 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class DSL < ConjunctionDSL
4
+
5
+ # Define an limit and/or offset for the results returned from the current
6
+ # filter. This method can only be called from the outermost scope of a filter
7
+ # (i.e. not inside of a having block, etc.). If it is called multiple times, the
8
+ # last one will override any others.
9
+ #
10
+ # ==== Parameters
11
+ # limit<Integer>::
12
+ # Used for the limit of the query.
13
+ # offset<Integer>::
14
+ # Used as the offset for the query. This argument is optional, with the default
15
+ # being no offset.
16
+ #
17
+ # ==== Returns
18
+ # nil
19
+ #
20
+ # @public
21
+ def limit(limit, offset=nil)
22
+ @conjunction.add_limit(limit, offset)
23
+ nil
24
+ end
25
+
26
+ # Define an offset for the results returned from the current
27
+ # filter. This method can only be called from the outermost scope of a filter
28
+ # (i.e. not inside of a having block, etc.). If it is called multiple times, the
29
+ # last one will override any others.
30
+ #
31
+ # ==== Parameters
32
+ # offset<Integer>::
33
+ # The offset of the query.
34
+ #
35
+ # ==== Returns
36
+ # nil
37
+ #
38
+ # @public
39
+ def offset(offset)
40
+ @conjunction.add_limit(nil, offset)
41
+ nil
42
+ end
43
+
44
+ # Define an order clause for the current query, with options for specifying
45
+ # both the column to use as well as the direction. This method can only be called
46
+ # in the outermost scope of a filter (i.e. not inside of a having block, etc.).
47
+ # Multiple calls will create multiple order clauses in the resulting query, and
48
+ # they will be added in the order in which they were called in the filter. In order
49
+ # to specify ordering on columns added through joins, a hash can be passed as the
50
+ # first argument, specifying a path through the joins to the column, as in this
51
+ # example:
52
+ #
53
+ # Blog.filter do
54
+ # having(:posts) do
55
+ # having(:comments).with(:created_at).greater_than(3.days.ago)
56
+ # end
57
+ # order(:posts => :comments => :created_at, :desc)
58
+ # order(:id, :asc)
59
+ # end
60
+ #
61
+ # ==== Parameters
62
+ # column<Symbol, Hash>::
63
+ # Specify the column for the ordering. If a symbol is given, it is assumed to represent
64
+ # a column in the class that is being filtered. With a hash argument, it is possible
65
+ # to specify a path to a column in one of the joined tables, as seen above. If a string
66
+ # is given and it doesn't match up with a column name, it is used as a literal string
67
+ # for ordering.
68
+ # direction<Symbol>::
69
+ # Specifies the direction of the order. Should be either :asc or :desc and defaults to :asc.
70
+ #
71
+ # ==== Returns
72
+ # nil
73
+ #
74
+ # ==== Raises
75
+ # InvalidFilterException::
76
+ # If the direction is neither :asc nor :desc.
77
+ #
78
+ # ==== Alternatives
79
+ # As described above, it is possible to pass a symbol, a hash or a string as the first
80
+ # argument.
81
+ #
82
+ # @public
83
+ def order(column, direction=:asc)
84
+ unless [:asc, :desc].include?(direction)
85
+ raise InvalidFilterException.new("The direction for orders must be either :asc or :desc but was #{direction}")
86
+ end
87
+ @conjunction.add_order(column, direction)
88
+ nil
89
+ end
90
+
91
+ # Specify a group_by clause for the resulting query. This method can only be called
92
+ # in the outermost scope of a filter (i.e. not inside of a having block, etc.).
93
+ # Multiple calls will create multiple group_by clauses in the resulting query, and
94
+ # they will be added in the order in which they were called in the filter. In order
95
+ # to specify grouping on columns added through joins, a hash can be passed as the
96
+ # argument, specifying a path through the joins to the column, as in this example:
97
+ #
98
+ # Blog.filter do
99
+ # having(:posts) do
100
+ # having(:comments).with(:created_at).greater_than(3.days.ago)
101
+ # end
102
+ # group_by(:posts => :comments => :offensive)
103
+ # group_by(:id)
104
+ # end
105
+ #
106
+ # ==== Parameters
107
+ # column<Symbol, Hash>::
108
+ # If a symbol is specified, it is taken to represent the name of a column on the
109
+ # class being filtered. If a hash is given, it should represent a path through the
110
+ # joins to a column in one of the joined tables. If a string is given, it is used
111
+ # without modification as the grouping parameter.
112
+ #
113
+ # ==== Returns
114
+ # nil
115
+ #
116
+ # ==== Alternatives
117
+ # As described above, it is possible to pass either a symbol, a hash, or a string
118
+ # as the argument.
119
+ #
120
+ # @public
121
+ def group_by(column)
122
+ @conjunction.add_group_by(column)
123
+ nil
124
+ end
125
+
126
+ # Specify that the resulting query should select distinct results. This method
127
+ # can only be called in the outermost scope of a filter (i.e. not inside of a having
128
+ # block, etc.).
129
+ #
130
+ # ==== Parameters
131
+ # none
132
+ #
133
+ # ==== Returns
134
+ # nil
135
+ #
136
+ # @public
137
+ def distinct
138
+ @conjunction.set_distinct
139
+ nil
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,19 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class DSLFactory # :nodoc: all
4
+ SUBCLASSES = Hash.new do |h, k|
5
+ h[k] = Class.new(RecordFilter::DSL::DSL)
6
+ end
7
+
8
+ class << self
9
+ def create(clazz)
10
+ get_subclass(clazz).new(clazz, Conjunction.new(clazz, :all_of))
11
+ end
12
+
13
+ def get_subclass(clazz)
14
+ SUBCLASSES[clazz.object_id]
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class GroupBy # :nodoc: all
4
+ attr_reader :column
5
+
6
+ def initialize(column)
7
+ @column = column
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class Join # :nodoc: all
4
+
5
+ attr_reader :association, :join_type, :conjunction, :aliaz
6
+
7
+ def initialize(association, join_type, conjunction, aliaz)
8
+ @association, @join_type, @conjunction, @aliaz = association, join_type, conjunction, aliaz
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class JoinCondition # :nodoc: all
4
+
5
+ attr_reader :restriction
6
+
7
+ def initialize(column, value)
8
+ @column = column
9
+ if column.is_a?(Hash) && value == Restriction::DEFAULT_VALUE
10
+ @condition = column
11
+ else
12
+ @restriction = Restriction.new(column, value)
13
+ end
14
+ end
15
+
16
+ def condition
17
+ @condition || restriction
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,49 @@
1
+ module RecordFilter
2
+ module DSL
3
+ # This class is used as the active DSL when executing the block provided to explicit
4
+ # joins created with ConjunctionDSL.join. It is a subclass of ConjunctionDSL that
5
+ # adds a method for specifying how the join is to be performed by giving either a pair
6
+ # of columns or a restriction that is applied to a column of the right table.
7
+ class JoinDSL < ConjunctionDSL
8
+
9
+ attr_reader :conditions # :nodoc:
10
+
11
+ # Specify parameters for explicit joins. This method should be called at least once
12
+ # within the block of a call to ConjunctionDSL.join and accepts various combinations
13
+ # of arguments to determine how to join the two tables. The possible options are:
14
+ #
15
+ # * Pass a hash where the key is a symbol that represents a column in the left table and the value is a symbol that represents a column in the right table.
16
+ # * Pass a symbol and a value, in which case an equality condition will be created on the column in the right table with the given column name.
17
+ # * Pass only the name of a column in the right table, in which case the method can be chained with any of the calls in the Restriction API to create generic restrictions on the column.
18
+ # Post.filter do
19
+ # join(Comment, :inner) do
20
+ # on(:id => :post_id)
21
+ # on(:offensive, false)
22
+ # on(:id).greater_than(12)
23
+ # end
24
+ #
25
+ # # Results in:
26
+ # # :joins => "INNER JOIN "comments" AS posts__Comment ON "posts".id = posts__Comment.post_id AND posts__Comment.offensive = false AND posts__Comment.id > 12"
27
+ # end
28
+ #
29
+ # ==== Parameters
30
+ # column<Hash, Symbol>::
31
+ # Either a hash representing the column pair to join or a symbol representing the column in the
32
+ # right table to add a condition to.
33
+ # value<value, optional>::
34
+ # If provided along with a symbol for the column argument, creates an equality condition on that
35
+ # column.
36
+ #
37
+ # ==== Returns
38
+ # Restriction::
39
+ # A restriction that can be used to limit the column when a symbol is passed as the column name.
40
+ #
41
+ # @public
42
+ def on(column, value=Restriction::DEFAULT_VALUE)
43
+ @conditions ||= []
44
+ @conditions << (condition = JoinCondition.new(column, value))
45
+ return condition.restriction
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,12 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class Limit # :nodoc: all
4
+
5
+ attr_reader :limit, :offset
6
+
7
+ def initialize(limit, offset)
8
+ @limit, @offset = limit, offset
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class NamedFilter # :nodoc: all
4
+
5
+ attr_reader :name, :args
6
+
7
+ def initialize(name, *args)
8
+ @name, @args = name, args
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module RecordFilter
2
+ module DSL
3
+ class Order # :nodoc: all
4
+
5
+ attr_reader :column, :direction
6
+
7
+ def initialize(column, direction)
8
+ @column, @direction = column, direction
9
+ end
10
+ end
11
+ end
12
+ end