record_filter 0.9.12

Sign up to get free protection for your applications and to get access to all the features.
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
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ coverage
2
+ perf
3
+ pkg
4
+ rdoc
5
+ *.gem
6
+ *.swp
7
+ tmp
8
+ _site
9
+
data/CHANGELOG ADDED
@@ -0,0 +1,232 @@
1
+ = 0.9.9
2
+
3
+ * Fixed a bug where distinct wasn't being applied when chaining with has_many
4
+ :through associations.
5
+
6
+ = 0.9.8
7
+
8
+ * BREAKING CHANGE: changed the 'limit' method so that the limit is always the
9
+ first argument, with an optional offset as the second argument. This makes the
10
+ functionality much clearer by avoiding obnoxious argument swapping.
11
+
12
+ * BREAKING CHANGE: changed the arguments to the 'join' method so that it
13
+ takes a class and an options hash. The options hash can contain a :join_type
14
+ and an :alias, allowing only the arguments that the client wants to use to be
15
+ specified. This will break existing code where the join type was passed as
16
+ the second argument.
17
+
18
+ * BREAKING CHANGE: changed the arguments to the 'having' method so that it
19
+ takes an association name and an options hash. The options hash takes
20
+ :join_type and :alias. This will break existing code where the join_type was
21
+ passed as the first argument to the method. The alias is a new option that
22
+ allows you to alias joins, which lets you join in the same association twice.
23
+
24
+ * Added a distinct method to force queries to be distinct.
25
+
26
+ = 0.9.7
27
+
28
+ * Fix a bug where explicit joins would not honor different aliases if joining
29
+ the same class twice.
30
+
31
+ = 0.9.6
32
+
33
+ * Build the alias for explicit join tables correctly for multi-word class
34
+ names.
35
+
36
+ * Set params[:readonly] to false by default. Not sure why we need to do that.
37
+
38
+ * If nil is passed as the argument to a comparison operator, don't fail with a
39
+ lack of bind variables, just do what AR does and compare against NULL.
40
+
41
+ * Add ability to order on columns in tables that were explicitly joined.
42
+
43
+ = 0.9.5
44
+
45
+ * Added 'and' and 'or' methods to the DSL::Restriction class so that you can do
46
+ things like with(:expired_at).gt(Time.now).or.is_null
47
+
48
+ = 0.9.4
49
+
50
+ * Stop forcing order to take a real column name and let it accept a string
51
+
52
+ * Added an offset method to the DSL so that we can do offsets separately from
53
+ limits
54
+
55
+ * Some small performance improvements
56
+
57
+ * Added a test to make sure that the IN restriction can take a range
58
+
59
+ * Changed the default rake task to run db:spec:prepare first
60
+
61
+ * Added metric_fu for code quality metrics tests
62
+
63
+ * Refactored a bunch of code to improve the metric numbers
64
+
65
+ = 0.9.3
66
+
67
+ * Fix a bug when using class_name on belongs_to associations without
68
+ foreign_key
69
+
70
+ = 0.9.2
71
+
72
+ * Ruby 1.9 compatability
73
+
74
+ = 0.9.1
75
+
76
+ * Change group_by so that it will not throw an exception for missing column
77
+ names, since we want to be able to group by arbitrary things.
78
+
79
+ = 0.9.0
80
+
81
+ * Added a github pages page with a quick intro.
82
+
83
+ * Add tests for working with default scopes.
84
+
85
+ * de-hackify the default aliases for explicit joins.
86
+
87
+ * Fix a bug where strings wouldn't work as the names of associations in
88
+ implicit joins.
89
+
90
+ * Improve the README
91
+
92
+ * Fix a bug when combining joins across multiple named filters that introduce
93
+ the same join.
94
+
95
+ = 0.8.0
96
+
97
+ * Raise an exception when calling order with something that isn't :asc or
98
+ :desc
99
+
100
+ * Use hanna for spiffy rdocs.
101
+
102
+ * Raise an exception when attempting to add a named filter to a class if there
103
+ is already an existing filter with the same name.
104
+
105
+ * Documentation.
106
+
107
+ * Refactored the custom DSL subclass creation code into a DSLFactory class.
108
+
109
+ * Added a (fairly simple) performance test.
110
+
111
+ * Get source_type option working for has_many.
112
+
113
+ * Correct hard-coded primary key columns.
114
+
115
+ = 0.6.0
116
+
117
+ * 100% test coverage. That's right, 100%.
118
+
119
+ * Added fixes for various options on AR associations.
120
+
121
+ * Support calling named filters from within joins in filters.
122
+
123
+ * Stop using _inheritable_attribute for accessing the named filters and just recurse for
124
+ them.
125
+
126
+ * Change the tests for named filters to use anonymous classes so that we're
127
+ sure we're starting from a blank slate.
128
+
129
+ * Make the API work correctly for anonymous classes.
130
+
131
+ * Correctly handle the case where nil or [] is passed as an argument to the IN
132
+ restriction.
133
+
134
+ * Added is_not_null as a nicer version of is_null.not
135
+
136
+ = 0.2.0
137
+
138
+ * Changed the available join types to inner, left and right.
139
+
140
+ * Fix a bug where we used the wrong type in polymorphic association joins.
141
+
142
+ * Use with_scope when chaining filters, named_filters, and AR associations in
143
+ order to keep the filter data instead of passing the query parameters directly
144
+ and combining them in order to make this work correctly with AR.
145
+
146
+ * Fix a bug when calling .filter in a chain of associations and named filters.
147
+
148
+ * Don't include conditions unless there are actually conditions specified.
149
+
150
+ * Fix a bug where the first and last methods would fail.
151
+
152
+ * Delegate array methods through to the result of the filter.
153
+
154
+ * Add proxy_options to mimic the API of named_scope for getting the options
155
+ that result from a given query.
156
+
157
+ * Support chaining with AR associations... (i.e. Blog.posts.published)
158
+
159
+ * Support has_many and has_one :through
160
+
161
+ = 0.1.4
162
+
163
+ * Join on both the id and type for polymorphic joins.
164
+
165
+ * Fix a bug where we were using the incorrect table names in joins and
166
+ ordering with joins.
167
+
168
+ * Change != to the SQL standard <>.
169
+
170
+ * Fix invalid SQL generated by not_all and not_any.
171
+
172
+ * Detect count/size/length queries and do the appropriate distinct clause for
173
+ those count queries.
174
+
175
+ * Do DISTINCT searches for queries that involve specific types of joins.
176
+
177
+ * Allow passing a join type as the first argument to having.
178
+
179
+ * Restructured the explicit join API
180
+
181
+ * Raise exceptions when doing an explicit join using columns that don't exist.
182
+
183
+ * Allow explicit joins on values: left_join(:class_name, :table_alias, :rcol
184
+ => 'Post'...)
185
+
186
+ * Added a filter_class method to the DSL for getting the class that is being
187
+ filtered.
188
+
189
+ * Added custom join feature: left_join(:table_name, :table_alias, :rcol =>
190
+ :lcol...)
191
+
192
+ * Changed custom exceptions to subclass StandardError and not crash the entire
193
+ app when throwing one (thanks RailsEnvy).
194
+
195
+ = 0.1.3
196
+
197
+ * Fixed a bug in filter.rb when trying to chain to named_scopes or things that
198
+ are otherwise not named_filters.
199
+
200
+ * Made a named_filters accessor for getting the list of filters that apply to
201
+ a particular class.
202
+
203
+ * Added none_of and not_all_of conjunctions.
204
+
205
+ * Changed the between restriction to take either a range, a tuple, or two
206
+ values.
207
+
208
+ * Support multiple joins in one having statement through having(:posts =>
209
+ :comments)
210
+
211
+ * Added a CHANGELOG
212
+
213
+ = 0.1.2
214
+
215
+ * Add LIKE and NOT LIKE restrictions
216
+
217
+ * Replace active record objects with their ids if passed as the value for a
218
+ resriction.
219
+
220
+ = 0.1.1
221
+
222
+ * Add group_by
223
+
224
+ * Raise informative exceptions when columns or associations are not found for
225
+ a given filter
226
+
227
+ * Alias is_null restriction to nil and null
228
+
229
+ * Alias comparison restrictions to gt, lt, lte, gte
230
+
231
+ * Add greater_than_or_equal_to and less_than_or_equal_to restrictions.
232
+
data/README.rdoc ADDED
@@ -0,0 +1,354 @@
1
+ = record_filter
2
+
3
+ record_filter is a DSL for specifying ActiveRecord queries in pure Ruby.
4
+ It has support for filters created on the fly and for named filters that are associated with object types.
5
+ record_filter has the following top-level features:
6
+
7
+ * Pure ruby API eliminates the need for hard-coded SQL in most cases.
8
+ * Works seamlessly with existing ActiveRecord APIs, including named scopes.
9
+ * Supports creation of ad-hoc filters as well as named filters that can be associated with object types.
10
+ * Allows chaining of filters with each other and with named scopes to create complex queries.
11
+ * Takes advantage of the associations in your ActiveRecord objects for a clean implicit join API.
12
+
13
+ == Documentation
14
+
15
+ The complete RDoc documentation is available at http://aub.github.com/record_filter/rdoc/. This page is
16
+ intended to be a getting started guide that should cover the most common uses.
17
+
18
+ == Installation
19
+
20
+ gem install aub-record_filter --source=http://gems.github.com
21
+
22
+ In Rails, you'll need to add this to your config/environment.rb file:
23
+
24
+ config.gem 'aub-record_filter', :lib => 'record_filter', :source => 'http://gems.github.com'
25
+
26
+ == Using Filters
27
+
28
+ Given a Blog model having a has_many relationship with a Post model, a simple
29
+ filter with conditions and joins might look like this.
30
+
31
+ Blog.filter do
32
+ with(:created_at).greater_than(1.day.ago)
33
+ having(:posts).with(:permalink, nil)
34
+ end
35
+
36
+ This could be expressed in ActiveRecord as:
37
+
38
+ Blog.all(
39
+ :joins => :posts,
40
+ :conditions => {
41
+ :posts => {:permalink => nil},
42
+ :created_at => 1.day.ago..Time.now
43
+ }
44
+ )
45
+
46
+ and it returns the same result, a list of Blog objects that are the result of the query. This
47
+ type of filter is designed to be created on the fly, but if you have a filter that you would like
48
+ to use in more than one place, it can be added to a class as a named filter. The following example
49
+ creates the same filter as above and executes it:
50
+
51
+ class Blog < ActiveRecord::Base
52
+ named_filter(:new_with_unlinked_posts) do
53
+ with(:created_at).greater_than(1.day.ago)
54
+ having(:posts).with(:permalink, nil)
55
+ end
56
+ end
57
+
58
+ Blog.new_with_unlinked_posts
59
+
60
+ This returns the same result as the example above but with the advantages that it is
61
+ easily reusable and that it can be combined with other named filters to produce a more
62
+ complex query:
63
+
64
+ class Post < ActiveRecord::Base
65
+ named_filter(:title_is_monkeys) { with(:title, 'monkeys') }
66
+ named_filter(:permalink_is_donkeys) { with(:permalink, 'donkeys') }
67
+ end
68
+
69
+ Post.title_is_monkeys.permalink_is_donkeys
70
+
71
+ This example will return all of the posts that meet both animal-related conditions.
72
+ There is no limit to the number of filters that can be combined, and because record_filter works
73
+ seamlessly with named scopes, they can also be combined in this way as well.
74
+
75
+ Named filters can also be customized by taking any number of arguments. The example above can
76
+ be replicated with the following filter:
77
+
78
+ class Post < ActiveRecord::Base
79
+ named_filter(:with_title_and_permalink) do |title, permalink|
80
+ with(:title, title)
81
+ with(:permalink, permalink)
82
+ end
83
+ end
84
+
85
+ Post.with_title_and_permalink('monkeys', 'donkeys')
86
+
87
+ Named filters can also be called from other named filters and will be invoked on the correct
88
+ model even if called from a join.
89
+
90
+ class Comment < ActiveRecord::Base
91
+ named_filter(:offensive) { with(:offensive, true) }
92
+ end
93
+
94
+ class Post < ActiveRecord::Base
95
+ named_filter(:using_other_filter) do
96
+ having(:comments) do
97
+ offensive
98
+ end
99
+ end
100
+ end
101
+
102
+ Post.using_other_filter
103
+
104
+ == Specifying Filters
105
+
106
+ record_filter supports all of the SQL query abstractions provided by ActiveRecord, specifically:
107
+
108
+ * Conditions
109
+ * Boolean operations
110
+ * Joins
111
+ * Limits
112
+ * Offsets
113
+ * Ordering
114
+ * Grouping
115
+
116
+ The following example shows the use of each of these techniques:
117
+
118
+ Post.filter do
119
+ any_of do
120
+ with(:permalink).is_null
121
+ having(:comments) do
122
+ with(:offensive, true)
123
+ end
124
+ end
125
+ limit(100, 10)
126
+ order(:created_at, :desc)
127
+ group_by(:comments => :offensive)
128
+ end
129
+
130
+ === Conditions
131
+
132
+ Conditions are specified using the 'with' function, which takes as its first argument
133
+ the name of the column to restrict. If a second argument is given, it will automatically
134
+ be used as the value in an equality condition. The 'with' function will return a Restriction
135
+ object that has methods to specify a number of different conditions and to negate them:
136
+
137
+ with(:permalink, 'aardvarks') # ['permalink = ?', 'aardvarks']
138
+ with(:permalink).equal_to('sheep') # ['permalink = ?', 'sheep']
139
+ with(:permalink).not.equal_to('cats') # ['permailnk <> ?', 'cats']
140
+
141
+ with(:permalink, nil) # ['permalink IS NULL']
142
+ with(:permalink).is_null # ['permalink IS NULL']
143
+ with(:permalink, nil).not # ['permalink IS NOT NULL']
144
+ with(:permalink).is_not_null # ['permalink IS NOT NULL']
145
+
146
+ The following condition types are supported through the Restriction API:
147
+
148
+ * Equality
149
+ * Comparisons (> >= < <=)
150
+ * Between
151
+ * In
152
+ * Is null
153
+ * Like
154
+ * Negation of all of the above
155
+
156
+ And here are some examples. See the RDoc page for
157
+ {DSL::Restriction}[http://aub.github.com/record_filter/rdoc/classes/RecordFilter/DSL/Restriction.html]
158
+ for more details on how to use them.
159
+
160
+ with(:featured_at).greater_than(Time.now) # ['featured_at > ?', Time.now]
161
+
162
+ with(:price).lte(1000) # ['price <= ?', 1000]
163
+
164
+ with(:created_at).between(time_a..time_b) # ['created_at BETWEEN ? AND ?', time_a, time_b]
165
+
166
+ with(:id).in([1, 2, 3]) # ['id in (?)', [1, 2, 3]]
167
+
168
+ with(:id).not_in([4, 5, 6]) # ['id NOT IN (?)', [4, 5, 6]]
169
+
170
+ with(:content).like('%easy%') # ['content LIKE ?', '%easy%']
171
+
172
+ with(:content).not.like('%hard%') # ['content NOT LIKE ?', '%hard%']
173
+
174
+ The comparison operators (greater_than, less_than, greater_than_or_equal_to and less_than_or_equal_to)
175
+ are aliased to their short forms (gt, lt, gte and lte).
176
+
177
+ It is also possible to specify multiple conditions on a single line using the 'and' and 'or' methods,
178
+ eliminating the need to use a conjunction block in many common cases.
179
+
180
+ with(:expired_at).gt(Time.now).or.is_null # ['expired_at > ? OR expired_at IS NULL', Time.now]
181
+
182
+ with(:id).gt(100).and.lt(1000) # ['id > ? AND id < ?', 100, 1000]
183
+
184
+ === Boolean Operations
185
+
186
+ Conditions can be combined with boolean operators using the methods all_of, any_of, none_of
187
+ and not_all_of. These methods take a block where any conditions they contain will be combined
188
+ using AND, OR and NOT to create the correct clause. The block can also contain any number of
189
+ joins or other boolean operations. The default operator is all_of.
190
+
191
+ Post.filter do
192
+ with(:id, 4)
193
+ with(:permalink, 'ack')
194
+ end
195
+
196
+ # ['id = ? AND permalink = ?', 4, 'ack']
197
+
198
+ Post.filter do
199
+ any_of do
200
+ with(:id, 3)
201
+ with(:permalink, 'booya')
202
+ end
203
+ end
204
+
205
+ # ['id = ? OR permalink = ?', 3, 'booya']
206
+
207
+ Post.filter do
208
+ none_of do
209
+ with(:id, 2)
210
+ with(:permalink, 'ouch')
211
+ end
212
+ end
213
+
214
+ # ['NOT (id = ? OR permalink = ?)', 2, 'ouch']
215
+
216
+ Post.filter do
217
+ not_all_of do
218
+ with(:id, 1)
219
+ with(:permalink, 'bonobo')
220
+ end
221
+ end
222
+
223
+ # ['NOT (id = ? AND permalink = ?)', 1, 'bonobo']
224
+
225
+ === Joins
226
+
227
+ Joins in record_filter come in two varieties. Using the information in ActiveRecord associations,
228
+ it is possible to perform most joins easily using the 'having' method, which requires no specification
229
+ of the columns to use for the join. In cases where an association does not apply, it is also possible
230
+ to create an explicit join that can include both the columns to combine as well as restrictions on
231
+ the columns in the join table.
232
+
233
+ In a filter for a Post model that has_many comments, the following two examples are equivalent:
234
+
235
+ having(:comments)
236
+
237
+ join(Comment, :join_type => :inner) do
238
+ on(:id => :post_id)
239
+ end
240
+
241
+ With an explicit join, any number of columns can be matched in this way, and both join types
242
+ accept a block in which any number of conditions, boolean operations, or other joins can be
243
+ added. Explicit joins also allow conditions to be set on columns of the table being joined:
244
+
245
+ having(:comments).with(:offensive, true)
246
+
247
+ having(:comments) do
248
+ with(:created_at).greater_than(2.days.ago)
249
+ end
250
+
251
+ join(Comment, :join_type => :inner) do
252
+ on(:id => :commentable_id)
253
+ on(:commentable_type).equal_to('Post')
254
+ with(:created_at).less_than(1.year.ago)
255
+ end
256
+
257
+ With implicit joins, it is also possible to use a hash as the association name, in which case
258
+ multiple joins can be created with one statement. If the comment model has_one Author, this
259
+ example will join both tables and add a condition on the author.
260
+
261
+ having(:comments => :author).with(:name, 'Bob')
262
+
263
+ For both join types, an options hash can be provided as the second argument for passing the join
264
+ type and/or an alias to use for the joined table. The join type defaults to :inner, and the alias
265
+ defaults to a unique name for identifying the table. Using aliases allows you to join to a given table
266
+ twice with two different names. How about a contrived example? Awesome.
267
+
268
+ Blog.filter do
269
+ having(:posts, :join_type => :left, :alias => 'posts_1').with(:title, 'a')
270
+ having(:posts, :alias => 'posts_2').with(:title, 'b')
271
+ end
272
+
273
+ # SELECT DISTINCT "blogs".* FROM "blogs"
274
+ # LEFT OUTER JOIN "posts" AS posts_1 ON "blogs".id = posts_1.blog_id
275
+ # INNER JOIN "posts" AS posts_2 ON "blogs".id = posts_2.blog_id
276
+ # WHERE ((posts_1.title = 'a') AND (posts_2.title = 'b'))
277
+
278
+ === Limits and Offsets
279
+
280
+ These are specified using the 'limit' method, which takes two arguments, the limit and the
281
+ offset. The offset is optional. For specifying only offsets, the 'offset' method is also available.
282
+
283
+ limit(100, 10) # :limit => 100, :offset => 100
284
+ limit(100) # :limit => 100
285
+ offset(10) # :offset => 10
286
+
287
+ === Ordering
288
+
289
+ Ordering is done through the 'order' method, which accepts arguments for the column and direction.
290
+ The column can either be passed as the name of a column in the class that is being filtered or as
291
+ a hash that represents a path through the joined associations to the correct column. The direction argument
292
+ should be either :asc or :desc and defaults to :asc if not given. Multiple calls to 'order' are
293
+ allowed and will be applied in the order in which they were given.
294
+
295
+ Post.filter do
296
+ having(:comments).with(:offensive, true)
297
+ order(:created_at, :desc)
298
+ order(:comments => :id)
299
+ end
300
+
301
+ # :order => "'posts'.created_at DESC posts__comments.id ASC"
302
+
303
+ === Grouping
304
+
305
+ Grouping is specified with the 'group_by' method, which accepts either the name of a column in the
306
+ class that is being filtered or a hash that represents a path through the joined associations. If
307
+ there are multiple calls to 'group_by' they will be combined in the final result, maintaining the
308
+ order in which they were given.
309
+
310
+ Post.filter do
311
+ having(:comments).with(:created_at).greater_than(1.hour.ago)
312
+ group_by(:permalink)
313
+ group_by(:comments => :offensive)
314
+ end
315
+
316
+ # :group => "'posts'.permalink, posts__comments.offensive'
317
+
318
+ === Distinct
319
+
320
+ Filters that include outer joins are automatically made distinct by record_filter. For filters that
321
+ use inner joins, use the 'distinct' method in the DSL to force the select to be distinct.
322
+
323
+ Blog.filter do
324
+ with(:created_at).greater_than(1.day.ago)
325
+ having(:posts).with(:permalink, nil)
326
+ distinct
327
+ end
328
+
329
+ # :select => "DISTINCT 'blogs'.*"
330
+
331
+ == LICENSE:
332
+
333
+ (The MIT License)
334
+
335
+ Copyright (c) 2008-2009 Aubrey Holland, Mat Brown
336
+
337
+ Permission is hereby granted, free of charge, to any person obtaining
338
+ a copy of this software and associated documentation files (the
339
+ 'Software'), to deal in the Software without restriction, including
340
+ without limitation the rights to use, copy, modify, merge, publish,
341
+ distribute, sublicense, and/or sell copies of the Software, and to
342
+ permit persons to whom the Software is furnished to do so, subject to
343
+ the following conditions:
344
+
345
+ The above copyright notice and this permission notice shall be
346
+ included in all copies or substantial portions of the Software.
347
+
348
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
349
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
350
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
351
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
352
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
353
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
354
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,92 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+
5
+ FileList['tasks/**/*.rake'].each { |file| load file }
6
+
7
+ task :default => ["db:spec:prepare", :spec]
8
+
9
+ begin
10
+ require 'jeweler'
11
+ Jeweler::Tasks.new do |gemspec|
12
+ gemspec.name = 'record_filter'
13
+ gemspec.summary = 'An ActiveRecord query API for replacing SQL with awesome'
14
+ gemspec.email = 'aubreyholland@gmail.com'
15
+ gemspec.homepage = 'http://github.com/aub/record_filter'
16
+ gemspec.add_dependency 'activerecord'
17
+ gemspec.add_development_dependency 'rspec'
18
+ gemspec.rubyforge_project = 'record-filter'
19
+ gemspec.description = 'RecordFilter is a Pure-ruby criteria API for building complex queries in ActiveRecord. It supports queries that are built on the fly as well as named filters that can be added to objects and chained to create complex queries. It also gets rid of the nasty hard-coded SQL that shows up in most ActiveRecord code with a clean API that makes queries simple and intuitive to build.'
20
+ gemspec.authors = ['Aubrey Holland', 'Mat Brown']
21
+ end
22
+ Jeweler::RubyforgeTasks.new do |rubyforge|
23
+ rubyforge.doc_task = 'rdoc'
24
+ end
25
+ rescue LoadError
26
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
27
+ end
28
+
29
+ # Try to use hanna to create spiffier docs.
30
+ begin
31
+ require 'hanna/rdoctask'
32
+ rescue LoadError
33
+ require 'rake/rdoctask'
34
+ end
35
+
36
+ Rake::RDocTask.new do |rdoc|
37
+ if File.exist?('VERSION.yml')
38
+ config = YAML.load(File.read('VERSION.yml'))
39
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
40
+ else
41
+ version = ""
42
+ end
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "record_filter #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ rdoc.options << '--webcvs=http://github.com/aub/record_filter/tree/master/'
49
+ end
50
+
51
+ begin
52
+ require 'ruby-prof/task'
53
+
54
+ RubyProf::ProfileTask.new do |t|
55
+ t.test_files = FileList['test/performance_test.rb']
56
+ t.output_dir = 'perf'
57
+ t.printer = :graph_html
58
+ t.min_percent = 5
59
+ end
60
+ rescue LoadError
61
+ puts 'Ruby-prof not available. Profiling tests are disabled.'
62
+ end
63
+
64
+ begin
65
+ require 'metric_fu'
66
+ MetricFu::Configuration.run do |config|
67
+ #define which metrics you want to use
68
+ config.metrics = [:churn, :flog, :flay, :reek, :roodi, :rcov] # :saikuro, :stats
69
+ config.flay = { :dirs_to_flay => ['lib'] }
70
+ config.flog = { :dirs_to_flog => ['lib'] }
71
+ config.reek = { :dirs_to_reek => ['lib'] }
72
+ config.roodi = { :dirs_to_roodi => ['lib'] }
73
+ config.saikuro = { :output_directory => 'scratch_directory/saikuro',
74
+ :input_directory => ['lib'],
75
+ :cyclo => "",
76
+ :filter_cyclo => "0",
77
+ :warn_cyclo => "5",
78
+ :error_cyclo => "7",
79
+ :formater => "text"} #this needs to be set to "text"
80
+ config.churn = { :start_date => "1 year ago", :minimum_churn_count => 10}
81
+ config.rcov = { :test_files => ['spec/**/*_spec.rb'],
82
+ :rcov_opts => ["--sort coverage",
83
+ "--no-html",
84
+ "--text-coverage",
85
+ "--no-color",
86
+ "--profile",
87
+ "--exclude spec"]}
88
+ end
89
+ rescue LoadError
90
+ puts 'Install metric_fu for code quality metric tests.'
91
+ end
92
+
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * Add ability to do custom selects.
2
+ * Add hooks for custom SQL in general.
3
+
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 9
3
+ :patch: 12
4
+ :major: 0