queryable_with 0.1.0

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/CHANGELOG ADDED
@@ -0,0 +1,5 @@
1
+ = QueryableWith Changelog
2
+
3
+ == 0.1.0
4
+
5
+ * First release. Supports query_set, queryable_with, and default_scope.
data/README.rdoc ADDED
@@ -0,0 +1,88 @@
1
+ = QueryableWith
2
+
3
+ QueryableWith is an ActiveRecord library for creating and combining reusable sets of scopes. Instead of building up
4
+ your query using if-conditions checking for the existence of certain parameters--a common use case in reporting and
5
+ controller query logic--QueryableWith combines them together, and only uses the ones the params you pass need.
6
+
7
+ For example,
8
+
9
+ class User < ActiveRecord::Base
10
+ named_scope :born_after, lambda {|date|
11
+ { :conditions => ["birthdate >= ?", date] }
12
+ }
13
+
14
+ named_scope :active, :conditions => { :active => true }
15
+
16
+ query_set :filter do
17
+ add_scope :active
18
+ queryable_with :email
19
+ queryable_with(:born_after) { |d| Date.parse(d) }
20
+ end
21
+ end
22
+
23
+ will add a method, <tt>User.filter</tt>, that knows how to filter on the <tt>email</tt> and <tt>born_after</tt>
24
+ parameters by, respectively, performing an equality search against that column, and using the defined name
25
+ scope (after passing the given date value, in this case a string, through the block). It will always add the
26
+ <tt>active</tt> scope. For example,
27
+
28
+ User.filter
29
+ #=> User.active
30
+
31
+ User.filter(:email => "gthreepwood@melee.gov")
32
+ #=> User.active.scoped(:conditions => { :email => "gthreepwood@melee.gov"})
33
+
34
+ User.filter(:born_after => "10/4/2010")
35
+ #=> User.active.born_after(#<Date: 4910947/2,0,2299161>)
36
+
37
+ See QueryableWith::ClassMethods#query_set for more information.
38
+
39
+ == Another Example
40
+
41
+ class User < ActiveRecord::Base
42
+ query_set :search do
43
+ # If queried by :name, execute a LIKE query on both the :first_name and :last_name columns.
44
+ # User.scoped(:conditions => ["(users.first_name LIKE ?) OR (users.last_name LIKE ?)", ...])
45
+ queryable_with :name, :columns => [ :first_name, :last_name ], :wildcard => true
46
+
47
+ # If queried by :username, execute a LIKE query on the the email column.
48
+ # User.scoped(:conditions => ["(users.email LIKE ?)", ...])
49
+ queryable_with :username, :column => :email, :wildcard => true
50
+ end
51
+ end
52
+
53
+ User.search(:name => ["Guy", "Three"])
54
+ #=> User.scoped(:conditions => ["(users.first_name LIKE ?) OR (users.last_name LIKE ?) OR ...", "%Guy%", "%Three%", "%Guy%", "%Three%"]
55
+
56
+ User.search(:username => "gthree")
57
+ #=> User.scoped(:conditions => { :email => "%gthree%"})
58
+
59
+ == Installation and usage
60
+
61
+ QueryableWith is available as a gem. It has a dependency on ActiveRecord, which it will automatically extend with
62
+ the necessary class methods when required. It is not yet compatible with Rails3.
63
+
64
+ gem install queryable_with
65
+ require 'queryable_with'
66
+
67
+ == License
68
+
69
+ Copyright (c) 2010 Brian Guthrie
70
+
71
+ Permission is hereby granted, free of charge, to any person obtaining
72
+ a copy of this software and associated documentation files (the
73
+ "Software"), to deal in the Software without restriction, including
74
+ without limitation the rights to use, copy, modify, merge, publish,
75
+ distribute, sublicense, and/or sell copies of the Software, and to
76
+ permit persons to whom the Software is furnished to do so, subject to
77
+ the following conditions:
78
+
79
+ The above copyright notice and this permission notice shall be
80
+ included in all copies or substantial portions of the Software.
81
+
82
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
83
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
84
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
85
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
86
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
87
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
88
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+
4
+ require 'spec'
5
+ require 'spec/rake/spectask'
6
+
7
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
8
+ require "queryable_with/version"
9
+
10
+ task :default => :spec
11
+
12
+ Spec::Rake::SpecTask.new do |t|
13
+ t.spec_opts = ['--colour', '--format progress', '--backtrace']
14
+ end
15
+
16
+ Rake::RDocTask.new do |rd|
17
+ rd.main = "README.rdoc"
18
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
19
+ end
20
+
21
+ namespace :gem do
22
+ task :clean do
23
+ system "rm -f *.gem"
24
+ end
25
+
26
+ task :build => :clean do
27
+ system "gem build queryable_with.gemspec"
28
+ end
29
+
30
+ task :release => :build do
31
+ system "gem push queryable_with-#{QueryableWith::Version::STRING}"
32
+ end
33
+ end
@@ -0,0 +1,194 @@
1
+ require 'active_record'
2
+
3
+ # See README.rdoc for an extended introduction. The QueryableWith::ClassMethods#query_set,
4
+ # QueryableWith::QuerySet#queryable_with and QueryableWith::QuerySet#add_scope methods may also
5
+ # be of interest.
6
+ module QueryableWith
7
+
8
+ def self.included(active_record) # :nodoc:
9
+ active_record.send :extend, QueryableWith::ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ def query_sets # :nodoc:
14
+ read_inheritable_attribute(:query_sets) || write_inheritable_hash(:query_sets, {})
15
+ end
16
+
17
+ # Defines a query set with the given name. This has the effect of defining a method that calls
18
+ # every implicit or explicit scope added to that query set.
19
+ #
20
+ # When a block is given, that block is evaluated in the context of either a newly-created query
21
+ # set or (in the case of inherited classes) the pre-existing set of that name. See
22
+ # QueryableWith::QuerySet#queryable_with and QueryableWith::QuerySet#add_scope for more details.
23
+ #
24
+ # When <tt>parent</tt> is given as an option, the new query set will inherit all of the scopes from
25
+ # the named parent.
26
+ def query_set(set_name, options={}, &block) # :yields:
27
+ set = query_set_for(set_name, options).tap { |s| s.instance_eval(&block) if block_given? }
28
+
29
+ class_eval <<-RUBY
30
+ def self.#{set_name}(params={})
31
+ self.query_sets["#{set_name}"].query(self, params)
32
+ end
33
+ RUBY
34
+ end
35
+
36
+ protected
37
+
38
+ def query_set_for(set_name, options) # :nodoc:
39
+ query_sets[set_name.to_s] || query_sets.store(set_name.to_s, QueryableWith::QuerySet.new(options))
40
+ end
41
+ end
42
+
43
+ class QuerySet
44
+
45
+ def initialize(options={}) # :nodoc:
46
+ @queryables = []
47
+
48
+ if options.has_key?(:parent)
49
+ @queryables << QueryableWith::ImplicitScopeParameter.new(options[:parent])
50
+ end
51
+ end
52
+
53
+ # Make this QuerySet queryable with the named parameter(s). If no other options are given,
54
+ # this will result in a query on either the column or (if defined) scope of the same
55
+ # name of the base scope and values passed to #query. It also accepts the following options:
56
+ #
57
+ # * <tt>scope</tt> - Map the incoming parameter to this scope. The argument by be a symbol (name of the
58
+ # scope), a Hash (scope conditions) or a lambda.
59
+ # * <tt>column</tt> - Map the incoming parameter to this column.
60
+ # * <tt>default</tt> - Default the incoming parameter to this value even if it isn't provided.
61
+ # * <tt>wildcard</tt> - If true, generate a SQL LIKE query with the incoming parameter. Used only
62
+ # if the <tt>scope</tt> option is absent or a block is not provided.
63
+ # * <tt>allow_blank</tt> - If true, treat incoming parameters mapped to nil or a blank string as
64
+ # IS NULL for the purposes of SQL query generation. Used only if the <tt>scope</tt> option is absent.
65
+ #
66
+ # If a block is provided, incoming parameters to the query will be passed through that function first.
67
+ # For example,
68
+ #
69
+ # queryable_with(:company_name, :scope => :by_company) do |name|
70
+ # Company.find_by_name(name)
71
+ # end
72
+ #
73
+ # will attempt to look up a company name first, then pass it to a pre-defined scope called
74
+ # <tt>by_company</tt>.
75
+ def queryable_with(*expected_parameters, &block)
76
+ options = expected_parameters.extract_options!
77
+
78
+ @queryables += expected_parameters.map do |parameter|
79
+ QueryableWith::QueryableParameter.new(parameter, options, &block)
80
+ end
81
+ end
82
+
83
+ # Add a scope that is always applied to any calls to #query. This may be a symbol (the name of
84
+ # the scope to add), a Hash (scope conditions) or a lambda. Useful when, for example, you only
85
+ # ever want to see records with an <tt>active</tt> flag set to true.
86
+ #
87
+ # add_scope :active
88
+ # add_scope :conditions => { :active => true }
89
+ def add_scope(scope)
90
+ @queryables << QueryableWith::ImplicitScopeParameter.new(scope)
91
+ end
92
+
93
+ # Applies all of the defined queryable and added scopes in this query set to the given base scope
94
+ # (usually an ActiveRecord class, but can also be a pre-existing NamedScope object) based on the
95
+ # query parameters and returns a new scope, ready to be queried.
96
+ def query(base_scope, params={}) # :nodoc:
97
+ @queryables.inject(base_scope) do |scope, queryer|
98
+ queryer.query(scope, params)
99
+ end.scoped({})
100
+ end
101
+
102
+ end
103
+
104
+ class ImplicitScopeParameter # :nodoc:
105
+
106
+ def initialize(scope)
107
+ @scope = scope
108
+ end
109
+
110
+ def query(queryer, params={})
111
+ if @scope.is_a? Symbol
112
+ queryer.send @scope, params
113
+ else
114
+ queryer.scoped @scope
115
+ end
116
+ end
117
+
118
+ end
119
+
120
+ class QueryableParameter # :nodoc:
121
+ attr_reader :expected_parameter, :column_name
122
+
123
+ def initialize(expected_parameter, options={}, &block)
124
+ @scope, @wildcard, @default_value, @allow_blank = options.values_at(:scope, :wildcard, :default, :allow_blank)
125
+ @expected_parameter = expected_parameter.to_sym
126
+ @column_name = options[:column] || @expected_parameter.to_s
127
+ @value_mapper = block || lambda {|o| o}
128
+ end
129
+
130
+ def scoped?; !@scope.blank?; end
131
+ def wildcard?; @wildcard == true; end
132
+ def blank_allowed?; @allow_blank == true; end
133
+ def has_default?; !@default_value.nil?; end
134
+ def scope_name; @scope || self.expected_parameter; end
135
+
136
+ def query(queryer, params={})
137
+ params = params.with_indifferent_access
138
+ params_contain_queryable_value, queried_value = determine_queried_value(params[@expected_parameter])
139
+
140
+ return queryer unless params_contain_queryable_value
141
+ queried_value = @value_mapper.call(queried_value)
142
+
143
+ if scoped? || queryer_scoped?(queryer)
144
+ queryer.send scope_name, queried_value
145
+ else
146
+ queryer.scoped(:conditions => conditions_for(queryer, queried_value))
147
+ end
148
+ end
149
+
150
+ protected
151
+
152
+ def determine_queried_value(value_in_params)
153
+ if value_in_params.blank? && value_in_params != false
154
+ if blank_allowed?
155
+ return true, nil
156
+ elsif has_default?
157
+ return true, @default_value
158
+ else
159
+ return false, nil
160
+ end
161
+ else
162
+ return true, value_in_params
163
+ end
164
+ end
165
+
166
+ def queryer_scoped?(queryer)
167
+ queryer.scopes.keys.include?(@expected_parameter)
168
+ end
169
+
170
+ def conditions_for(queryer, value)
171
+ query_string = if wildcard?
172
+ "(#{queryer.table_name}.#{self.column_name} LIKE ?)"
173
+ else
174
+ "(#{queryer.table_name}.#{self.column_name} = ?)"
175
+ end
176
+
177
+ final_values = [ value ].flatten.map do |value|
178
+ wildcard? ? "%#{value}%" : value
179
+ end
180
+
181
+ final_query_string = ( [ query_string ] * final_values.size ).join(" OR ")
182
+
183
+ [ final_query_string ] + final_values
184
+ end
185
+
186
+ end
187
+
188
+ end
189
+
190
+ module ActiveRecord # :nodoc:
191
+ class Base # :nodoc:
192
+ include QueryableWith
193
+ end
194
+ end
@@ -0,0 +1,9 @@
1
+ module QueryableWith #:nodoc:
2
+ module Version #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,239 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ describe QueryableWith do
4
+ before :each do
5
+ User.delete_all
6
+ User.query_sets.clear
7
+ end
8
+
9
+ it "includes itself in ActiveRecord" do
10
+ ActiveRecord::Base.ancestors.should include(QueryableWith)
11
+ end
12
+
13
+ describe "query_set" do
14
+ it "exposes a new method named after the defined query set" do
15
+ User.query_set :test
16
+ User.methods.should include("test")
17
+ end
18
+
19
+ it "returns a scope that points to an empty result set if the query set is empty" do
20
+ User.query_set :test
21
+ User.test.should == [ ]
22
+ end
23
+
24
+ describe ":parent => [query_set_name]" do
25
+ it "applies a parent set that adds a scope" do
26
+ User.query_set(:test) { add_scope(:conditions => { :active => true }) }
27
+ User.query_set(:supertest, :parent => :test) { add_scope(:conditions => { :name => "Guybrush" }) }
28
+
29
+ active_guy = User.create! :name => "Guybrush", :active => true
30
+ inactive_guy = User.create! :name => "Guybrush", :active => false
31
+
32
+ User.supertest.should == [ active_guy ]
33
+ end
34
+
35
+ it "applies a parent set that is itself queryable" do
36
+ User.query_set(:test) { queryable_with(:name) }
37
+ User.query_set(:supertest, :parent => :test) { add_scope(:conditions => { :active => true }) }
38
+
39
+ active_guy = User.create! :name => "Guybrush", :active => true
40
+ inactive_guy = User.create! :name => "Guybrush", :active => false
41
+ active_gal = User.create! :name => "Elaine", :active => true
42
+
43
+ User.supertest(:name => "Guybrush").should == [ active_guy ]
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "queryable_with" do
49
+ it "maps a parameter directly to a column" do
50
+ guybrush = User.create! :name => "Guybrush"
51
+ elaine = User.create! :name => "Elaine"
52
+ User.query_set(:test) { queryable_with(:name) }
53
+
54
+ User.test(:name => "Guybrush").should == [ guybrush ]
55
+ end
56
+
57
+ it "maps multiple parameters, each to their own column" do
58
+ guybrush = User.create! :name => "Guybrush", :active => true
59
+ elaine = User.create! :name => "Elaine", :active => false
60
+ User.query_set(:test) { queryable_with(:name, :active) }
61
+
62
+ # User.test(:name => "Guybrush").should == [ guybrush ]
63
+ User.test(:active => false).should == [ elaine ]
64
+ end
65
+
66
+ it "uses the correct table name when referring to columns with potentially conflicting names" do
67
+ # The employers table also has a name column.
68
+ User.query_set(:test) { queryable_with(:name) }
69
+
70
+ lambda do
71
+ User.test(:name => "Guybrush").all(:joins => :employer)
72
+ end.should_not raise_error(ActiveRecord::StatementInvalid)
73
+ end
74
+
75
+ it "selects any one of a given list of values" do
76
+ guybrush = User.create! :name => "Guybrush"
77
+ elaine = User.create! :name => "Elaine"
78
+ User.query_set(:test) { queryable_with(:name) }
79
+
80
+ User.test(:name => ["Guybrush", "Elaine"]).should include(guybrush, elaine)
81
+ end
82
+
83
+ it "returns a scope that points to an empty result set if queried with an empty set of params" do
84
+ User.query_set(:test) { queryable_with(:name) }
85
+ User.test.should == [ ]
86
+ end
87
+
88
+ it "applies multiple parameters if multiple parameters are queried" do
89
+ active_bob = User.create! :name => "Bob", :active => true
90
+ lazy_bob = User.create! :name => "Bob", :active => false
91
+ User.query_set(:test) { queryable_with(:name, :active) }
92
+
93
+ User.test(:name => "Bob", :active => true).should == [ active_bob ]
94
+ end
95
+
96
+ it "delegates the underlying filter to a pre-existing named scope if one of that name is defined" do
97
+ guybrush = User.create! :name => "Guybrush"
98
+ User.named_scope(:naym, lambda { |name| { :conditions => { :name => name } } })
99
+ User.query_set(:test) { queryable_with(:naym) }
100
+
101
+ User.test(:naym => "Guybrush").should == [ guybrush ]
102
+ User.test(:naym => "Herrman").should == [ ]
103
+ end
104
+
105
+ it "ignores blank values" do
106
+ guybrush = User.create! :name => "Guybrush"
107
+ User.query_set(:test) { queryable_with(:name) }
108
+ User.test(:name => "").should == [ guybrush ]
109
+ User.test(:name => nil).should == [ guybrush ]
110
+ end
111
+
112
+ describe ":scope => [scope_name]" do
113
+ it "delegates the underlying filter to a pre-existing scope if passed as an option" do
114
+ guybrush = User.create! :name => "Guybrush"
115
+ User.named_scope(:naym, lambda { |name| { :conditions => { :name => name } } })
116
+ User.query_set(:test) { queryable_with(:nizame, :scope => :naym) }
117
+
118
+ User.test(:nizame => "Guybrush").should == [ guybrush ]
119
+ User.test(:nizame => "Herrman").should == [ ]
120
+ end
121
+ end
122
+
123
+ describe ":wildcard => true" do
124
+ it "wildcards the given value" do
125
+ guybrush = User.create! :name => "Guybrush"
126
+ User.query_set(:test) { queryable_with(:name, :wildcard => true) }
127
+
128
+ User.test(:name => "uybru").should == [ guybrush ]
129
+ User.test(:name => "Elaine").should == [ ]
130
+ end
131
+
132
+ it "ORs multiple wildcarded values" do
133
+ guybrush = User.create! :name => "Guybrush"
134
+ elaine = User.create! :name => "Elaine"
135
+ User.query_set(:test) { queryable_with(:name, :wildcard => true) }
136
+
137
+ User.test(:name => ["uybru", "lain"]).should include(guybrush, elaine)
138
+ end
139
+ end
140
+
141
+ describe ":column => [column_name]" do
142
+ it "maps the parameter to the given column name" do
143
+ guybrush = User.create! :name => "Guybrush"
144
+ elaine = User.create! :name => "Elaine"
145
+ User.query_set(:test) { queryable_with(:naym, :column => :name) }
146
+
147
+ User.test(:naym => "Guybrush").should == [ guybrush ]
148
+ end
149
+ end
150
+
151
+ describe ":default => [value]" do
152
+ it "provides a default value if none is given in the query" do
153
+ guybrush = User.create! :name => "Guybrush"
154
+ elaine = User.create! :name => "Elaine"
155
+ User.query_set(:test) { queryable_with(:name, :default => "Guybrush") }
156
+
157
+ User.test.should == [ guybrush ]
158
+ end
159
+ end
160
+
161
+ describe ":allow_blank => [boolean]" do
162
+ it "accepts, and queries on, blank values" do
163
+ guybrush = User.create! :name => "Guybrush"
164
+ User.query_set(:test) { queryable_with(:name, :allow_blank => true) }
165
+ User.test(:name => "").should == [ ]
166
+ User.test(:name => nil).should == [ ]
167
+ end
168
+ end
169
+
170
+ describe "with a block" do
171
+ it "permits you to transform the incoming value with a standard column lookup" do
172
+ guybrush = User.create! :name => "Guybrush"
173
+ elaine = User.create! :name => "Elaine"
174
+ User.query_set(:test) { queryable_with(:name) { |str| str.gsub(/fawkes/, "brush") } }
175
+
176
+ User.test(:name => "Guyfawkes").should == [ guybrush ]
177
+ end
178
+
179
+ it "permits you to transform the incoming value with a scope" do
180
+ guybrush1 = User.create! :name => "Guybrush", :employer => Employer.create!(:name => "Threepwood Nautical Services LLC")
181
+ guybrush2 = User.create! :name => "Guybrush", :employer => Employer.create!(:name => "LeChuck LeLumber, Inc.")
182
+
183
+ User.named_scope :by_employer, lambda { |employer|
184
+ { :conditions => { :employer_id => employer } }
185
+ }
186
+
187
+ User.query_set(:test) do
188
+ queryable_with :employer_name, :scope => :by_employer do |name|
189
+ Employer.find_by_name(name)
190
+ end
191
+ end
192
+
193
+ User.test(:employer_name => "Threepwood Nautical Services LLC").should == [ guybrush1 ]
194
+ end
195
+ end
196
+ end
197
+
198
+ describe "add_scope" do
199
+ it "adds the given named scope to every query" do
200
+ active = User.create! :active => true
201
+ inactive = User.create! :active => false
202
+ User.named_scope(:only_active, :conditions => { :active => true })
203
+ User.query_set(:test) { add_scope(:only_active) }
204
+
205
+ User.test.all.should == [ active ]
206
+ end
207
+
208
+ it "adds the given ad hoc scope to every query" do
209
+ active = User.create! :active => true
210
+ inactive = User.create! :active => false
211
+ User.query_set(:test) { add_scope(:conditions => { :active => true }) }
212
+
213
+ User.test.all.should == [ active ]
214
+ end
215
+ end
216
+
217
+ describe "(subclassed)" do
218
+ it "inherits the query sets from its superclass" do
219
+ User.query_set(:test) { add_scope(:conditions => { :active => true }) }
220
+ class Pirate < User; end
221
+ active = Pirate.create! :active => true
222
+ inactive = Pirate.create! :active => false
223
+
224
+ Pirate.test.should == [ active ]
225
+ end
226
+
227
+ it "allows query sets to be extended by the subclass" do
228
+ User.query_set(:test) { add_scope(:conditions => { :active => true }) }
229
+ class Pirate < User; end
230
+ Pirate.query_set(:test) { add_scope(:conditions => { :name => "Guybrush" }) }
231
+
232
+ active_guy = Pirate.create! :name => "Guybrush", :active => true
233
+ inactive_guy = Pirate.create! :name => "Guybrush", :active => false
234
+
235
+ Pirate.test.should == [ active_guy ]
236
+ end
237
+ end
238
+
239
+ end
@@ -0,0 +1,37 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'spec'
5
+ require 'queryable_with'
6
+
7
+ LOGFILE = File.open(File.dirname(__FILE__) + '/../tmp/database.log', 'a')
8
+ ActiveRecord::Base.logger = Logger.new(LOGFILE)
9
+ ActiveRecord::Base.configurations = true
10
+ ActiveRecord::Schema.verbose = false
11
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
12
+
13
+ ActiveRecord::Schema.define(:version => 1) do
14
+ create_table :users do |t|
15
+ t.string "name"
16
+ t.date "birthdate"
17
+ t.string "email"
18
+ t.string "join_date"
19
+ t.integer "income"
20
+ t.integer "employer_id"
21
+ t.string "type"
22
+ t.boolean "active"
23
+ end
24
+
25
+ create_table :employers do |t|
26
+ t.string "name"
27
+ t.string "email"
28
+ t.timestamps
29
+ end
30
+ end
31
+
32
+ class User < ActiveRecord::Base
33
+ belongs_to :employer
34
+ end
35
+
36
+ class Employer < ActiveRecord::Base
37
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: queryable_with
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Brian Guthrie
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-16 00:00:00 +05:30
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 3
30
+ - 0
31
+ version: 2.3.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :development
45
+ version_requirements: *id002
46
+ description: Tie query parameters to scopes, or create dynamic scopes on the fly. Define sets of reusable scopes for use in reporting and filtering.
47
+ email:
48
+ - btguthrie@gmail.com
49
+ executables: []
50
+
51
+ extensions: []
52
+
53
+ extra_rdoc_files: []
54
+
55
+ files:
56
+ - lib/queryable_with/version.rb
57
+ - lib/queryable_with.rb
58
+ - README.rdoc
59
+ - CHANGELOG
60
+ - Rakefile
61
+ has_rdoc: true
62
+ homepage: http://github.com/bguthrie/queryable_with
63
+ licenses: []
64
+
65
+ post_install_message:
66
+ rdoc_options: []
67
+
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.3.6
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: An ActiveRecord library for creating reusable sets of scopes.
91
+ test_files:
92
+ - spec/queryable_with_spec.rb
93
+ - spec/spec_helper.rb