pacecar 1.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +22 -0
- data/README.rdoc +202 -0
- data/init.rb +1 -0
- data/lib/pacecar.rb +32 -0
- data/lib/pacecar/associations.rb +39 -0
- data/lib/pacecar/boolean.rb +34 -0
- data/lib/pacecar/datetime.rb +64 -0
- data/lib/pacecar/duration.rb +28 -0
- data/lib/pacecar/helpers.rb +50 -0
- data/lib/pacecar/limit.rb +22 -0
- data/lib/pacecar/order.rb +24 -0
- data/lib/pacecar/polymorph.rb +17 -0
- data/lib/pacecar/presence.rb +23 -0
- data/lib/pacecar/ranking.rb +26 -0
- data/lib/pacecar/search.rb +42 -0
- data/lib/pacecar/state.rb +36 -0
- metadata +70 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2008, Matt Jankowski & thoughtbot, inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
== Pacecar
|
2
|
+
|
3
|
+
Pacecar adds named_scope methods and other common functionality to ActiveRecord classes via database column introspection.
|
4
|
+
|
5
|
+
Pacecar automatically includes the Pacecar::Helpers module into all ActiveRecord::Base classes.
|
6
|
+
|
7
|
+
To get all Pacecar functionality, you need to "include Pacecar" in your class.
|
8
|
+
|
9
|
+
To get some subset (for example, only the state functionality), you can do something like "include Pacecar::State" to get only the module(s) you want.
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
|
13
|
+
Assuming a database schema...
|
14
|
+
|
15
|
+
class CreateSchema < ActiveRecord::Migration
|
16
|
+
def self.up
|
17
|
+
create_table :users, :force => true do |t|
|
18
|
+
t.boolean :admin, :default => false, :null => false
|
19
|
+
t.datetime :approved_at
|
20
|
+
t.datetime :rejected_at
|
21
|
+
t.string :first_name
|
22
|
+
t.string :last_name
|
23
|
+
t.text :description
|
24
|
+
t.timestamps
|
25
|
+
end
|
26
|
+
create_table :posts, :force => true do |t|
|
27
|
+
t.string :owner_type
|
28
|
+
t.integer :owner_id
|
29
|
+
t.string :publication_state
|
30
|
+
t.string :post_type
|
31
|
+
t.timestamps
|
32
|
+
end
|
33
|
+
create_table :comments, :force => true do |t|
|
34
|
+
t.integer :user_id
|
35
|
+
t.text :description
|
36
|
+
t.timestamps
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
And some basic model declarations...
|
42
|
+
|
43
|
+
class User < ActiveRecord::Base
|
44
|
+
include Pacecar
|
45
|
+
has_many :posts, :as => :owner
|
46
|
+
has_many :comments
|
47
|
+
has_many :articles
|
48
|
+
has_ranking :comments
|
49
|
+
has_recent_records :comments
|
50
|
+
has_recent_records :articles, :comments
|
51
|
+
end
|
52
|
+
|
53
|
+
class Post < ActiveRecord::Base
|
54
|
+
include Pacecar
|
55
|
+
PUBLICATION_STATES = %w(Draft Submitted Rejected Accepted)
|
56
|
+
TYPES = %w(Free Open Private Anonymous PostModern)
|
57
|
+
belongs_to :owner, :polymorphic => true
|
58
|
+
has_state :publication_state
|
59
|
+
has_state :post_type, :with => TYPES
|
60
|
+
has_polymorph :owner
|
61
|
+
end
|
62
|
+
|
63
|
+
class Comment < ActiveRecord::Base
|
64
|
+
include Pacecar
|
65
|
+
belongs_to :user
|
66
|
+
end
|
67
|
+
|
68
|
+
class Article < ActiveRecord::Base
|
69
|
+
belongs_to :user
|
70
|
+
end
|
71
|
+
|
72
|
+
= All columns
|
73
|
+
|
74
|
+
Records where approved_at is not null, or where it is null...
|
75
|
+
|
76
|
+
User.approved_at_present
|
77
|
+
User.approved_at_missing
|
78
|
+
|
79
|
+
Records where first_name is not null, or where it is null...
|
80
|
+
|
81
|
+
User.first_name_present
|
82
|
+
User.first_name_missing
|
83
|
+
|
84
|
+
Records ordered by first_name (default to 'asc', can specify to override)...
|
85
|
+
|
86
|
+
User.by_first_name
|
87
|
+
User.by_first_name(:asc)
|
88
|
+
User.by_first_name(:desc)
|
89
|
+
|
90
|
+
Records where an attribute matches a search term (column LIKE "%term%")...
|
91
|
+
|
92
|
+
User.first_name_matches('John')
|
93
|
+
|
94
|
+
Records where an attribute starts or ends with a search term...
|
95
|
+
|
96
|
+
User.first_name_starts_with('A')
|
97
|
+
User.first_name_ends_with('a')
|
98
|
+
|
99
|
+
Records where any non-state text or string column matches term...
|
100
|
+
|
101
|
+
User.search_for('test')
|
102
|
+
|
103
|
+
Records where any of a list of columns match the term...
|
104
|
+
|
105
|
+
User.search_for 'test', :on => [:first_name, :last_name]
|
106
|
+
|
107
|
+
Records where all of a list of columns match the term...
|
108
|
+
|
109
|
+
User.search_for 'test', :on => [:first_name, :last_name], :require => :all
|
110
|
+
|
111
|
+
= Boolean columns
|
112
|
+
|
113
|
+
Records that are all admins or non-admins...
|
114
|
+
|
115
|
+
User.admin
|
116
|
+
User.not_admin
|
117
|
+
|
118
|
+
The "balance" (count of true minus false for column in question)...
|
119
|
+
|
120
|
+
User.admin_balance
|
121
|
+
|
122
|
+
= Datetime columns
|
123
|
+
|
124
|
+
Records approved before or after certain times...
|
125
|
+
|
126
|
+
User.approved_at_before(5.days.ago)
|
127
|
+
User.approved_at_after(4.weeks.ago)
|
128
|
+
|
129
|
+
Records with approved_at in the past or future...
|
130
|
+
|
131
|
+
User.approved_at_in_past
|
132
|
+
User.approved_at_in_future
|
133
|
+
|
134
|
+
Records with approved_at inside or outside of two times...
|
135
|
+
|
136
|
+
User.approved_at_inside(10.days.ago, 1.day.ago)
|
137
|
+
User.approved_at_outside(2.days.ago, 1.day.ago)
|
138
|
+
|
139
|
+
Records with certain year, month or day...
|
140
|
+
|
141
|
+
User.approved_at_in_year(2000)
|
142
|
+
User.approved_at_in_month(01)
|
143
|
+
User.approved_at_in_day(01)
|
144
|
+
|
145
|
+
Records with a duration (time delta between two columns) of, over or under a certain number of days...
|
146
|
+
|
147
|
+
User.with_duration_of(14, :approved_at, :rejected_at)
|
148
|
+
User.with_duration_over(14, :approved_at, :rejected_at)
|
149
|
+
User.with_duration_under(14, :approved_at, :rejected_at)
|
150
|
+
|
151
|
+
= Polymorphic relationships
|
152
|
+
|
153
|
+
Records which have an owner_type of User...
|
154
|
+
|
155
|
+
Post.for_owner_type(User)
|
156
|
+
|
157
|
+
= Associations
|
158
|
+
|
159
|
+
Records with the most and least associated records...
|
160
|
+
|
161
|
+
User.maximum_comments
|
162
|
+
User.minimum_comments
|
163
|
+
|
164
|
+
Records with associated records since a certain time...
|
165
|
+
|
166
|
+
User.recent_comments_since(2.days.ago)
|
167
|
+
User.recent_comments_and_posts_since(3.days.ago)
|
168
|
+
User.recent_comments_or_posts_since(4.days.ago)
|
169
|
+
|
170
|
+
= State columns
|
171
|
+
|
172
|
+
Records which are in a particular state, or not in a state...
|
173
|
+
|
174
|
+
Post.publication_state_draft
|
175
|
+
Post.post_type_not_open
|
176
|
+
|
177
|
+
Query methods on instances to check state...
|
178
|
+
|
179
|
+
Post.first.publication_state_draft?
|
180
|
+
Post.last.post_type_not_open?
|
181
|
+
|
182
|
+
= Limits
|
183
|
+
|
184
|
+
First x records...
|
185
|
+
|
186
|
+
User.limited(10)
|
187
|
+
|
188
|
+
= Named scopes
|
189
|
+
|
190
|
+
Because these are all named_scope, you can combine them.
|
191
|
+
|
192
|
+
To get all users that have a first_name set, who are admins and approved more than 2 weeks ago, ordered by their first name...
|
193
|
+
|
194
|
+
User.first_name_present.admin.approved_at_before(2.weeks.ago).by_first_name
|
195
|
+
|
196
|
+
To get the top 10 commenters...
|
197
|
+
|
198
|
+
User.maximim_comments.limited(10)
|
199
|
+
|
200
|
+
= License
|
201
|
+
|
202
|
+
Pacecar is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'pacecar'
|
data/lib/pacecar.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'pacecar/associations'
|
2
|
+
require 'pacecar/boolean'
|
3
|
+
require 'pacecar/datetime'
|
4
|
+
require 'pacecar/duration'
|
5
|
+
require 'pacecar/helpers'
|
6
|
+
require 'pacecar/limit'
|
7
|
+
require 'pacecar/order'
|
8
|
+
require 'pacecar/polymorph'
|
9
|
+
require 'pacecar/presence'
|
10
|
+
require 'pacecar/ranking'
|
11
|
+
require 'pacecar/search'
|
12
|
+
require 'pacecar/state'
|
13
|
+
|
14
|
+
module Pacecar
|
15
|
+
def self.included(base)
|
16
|
+
base.class_eval do
|
17
|
+
include Pacecar::Associations
|
18
|
+
include Pacecar::Boolean
|
19
|
+
include Pacecar::Datetime
|
20
|
+
include Pacecar::Duration
|
21
|
+
include Pacecar::Limit
|
22
|
+
include Pacecar::Order
|
23
|
+
include Pacecar::Polymorph
|
24
|
+
include Pacecar::Presence
|
25
|
+
include Pacecar::Ranking
|
26
|
+
include Pacecar::Search
|
27
|
+
include Pacecar::State
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
ActiveRecord::Base.send :include, Pacecar::Helpers
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Associations
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def has_recent_records(*names)
|
10
|
+
names.each do |name|
|
11
|
+
named_scope "recent_#{name}_since".to_sym, lambda { |since|
|
12
|
+
{
|
13
|
+
:conditions => [conditions_for_name(name), { :since_time => since }]
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
unless names.first == names.last
|
18
|
+
named_scope "recent_#{names.join('_or_')}_since".to_sym, lambda { |since|
|
19
|
+
{
|
20
|
+
:conditions => [names.collect { |name| conditions_for_name(name) }.join(' or '), { :since_time => since }]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
named_scope "recent_#{names.join('_and_')}_since".to_sym, lambda { |since|
|
24
|
+
{
|
25
|
+
:conditions => [names.collect { |name| conditions_for_name(name) }.join(' and '), { :since_time => since }]
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def conditions_for_name(name)
|
34
|
+
"((select count(*) from \"#{name}\" where \"#{name}\".#{reflections[name].primary_key_name} = #{quoted_table_name}.id and \"#{name}\".created_at > :since_time) > 0)"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Boolean
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_boolean_scopes
|
10
|
+
base.send :define_balance_count
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def define_boolean_scopes
|
16
|
+
boolean_column_names.each do |name|
|
17
|
+
named_scope name.to_sym, :conditions => ["#{quoted_table_name}.#{name} = ?", true]
|
18
|
+
named_scope "not_#{name}".to_sym, :conditions => ["#{quoted_table_name}.#{name} = ?", false]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def define_balance_count
|
23
|
+
boolean_column_names.each do |name|
|
24
|
+
self.class_eval %Q{
|
25
|
+
def self.#{name}_balance
|
26
|
+
#{name}.count - not_#{name}.count
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Datetime
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_datetime_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_datetime_scopes
|
15
|
+
datetime_column_names.each do |name|
|
16
|
+
define_before_after_scopes(name)
|
17
|
+
define_past_future_scopes(name)
|
18
|
+
define_inside_outside_scopes(name)
|
19
|
+
define_in_date_scopes(name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def define_before_after_scopes(name)
|
24
|
+
named_scope "#{name}_before".to_sym, lambda { |time|
|
25
|
+
{ :conditions => ["#{quoted_table_name}.#{name} < ?", time] }
|
26
|
+
}
|
27
|
+
named_scope "#{name}_after".to_sym, lambda { |time|
|
28
|
+
{ :conditions => ["#{quoted_table_name}.#{name} > ?", time] }
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_past_future_scopes(name)
|
33
|
+
named_scope "#{name}_in_past", lambda {
|
34
|
+
{ :conditions => ["#{quoted_table_name}.#{name} < ?", Time.now] }
|
35
|
+
}
|
36
|
+
named_scope "#{name}_in_future", lambda {
|
37
|
+
{ :conditions => ["#{quoted_table_name}.#{name} > ?", Time.now] }
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def define_inside_outside_scopes(name)
|
42
|
+
named_scope "#{name}_inside".to_sym, lambda { |start, stop|
|
43
|
+
{ :conditions => ["#{quoted_table_name}.#{name} > ? and #{quoted_table_name}.#{name} < ?", start, stop] }
|
44
|
+
}
|
45
|
+
named_scope "#{name}_outside".to_sym, lambda { |start, stop|
|
46
|
+
{ :conditions => ["#{quoted_table_name}.#{name} < ? and #{quoted_table_name}.#{name} > ?", start, stop] }
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_in_date_scopes(name)
|
51
|
+
named_scope "#{name}_in_year".to_sym, lambda { |year|
|
52
|
+
{ :conditions => ["year(#{quoted_table_name}.#{name}) = ?", year] }
|
53
|
+
}
|
54
|
+
named_scope "#{name}_in_month".to_sym, lambda { |month|
|
55
|
+
{ :conditions => ["month(#{quoted_table_name}.#{name}) = ?", month] }
|
56
|
+
}
|
57
|
+
named_scope "#{name}_in_day".to_sym, lambda { |day|
|
58
|
+
{ :conditions => ["day(#{quoted_table_name}.#{name}) = ?", day] }
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Duration
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_duration_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_duration_scopes
|
15
|
+
named_scope :with_duration_of, lambda { |duration, start, stop|
|
16
|
+
{ :conditions => ["datediff(#{quoted_table_name}.#{start}, #{quoted_table_name}.#{stop}) = ?", duration] }
|
17
|
+
}
|
18
|
+
named_scope :with_duration_over, lambda { |duration, start, stop|
|
19
|
+
{ :conditions => ["datediff(#{quoted_table_name}.#{start}, #{quoted_table_name}.#{stop}) > ?", duration] }
|
20
|
+
}
|
21
|
+
named_scope :with_duration_under, lambda { |duration, start, stop|
|
22
|
+
{ :conditions => ["datediff(#{quoted_table_name}.#{start}, #{quoted_table_name}.#{stop}) < ?", duration] }
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
mattr_accessor :options
|
5
|
+
self.options = {
|
6
|
+
:state_pattern => /_(type|state)$/i,
|
7
|
+
:default_limit => 10
|
8
|
+
}
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
def safe_columns
|
17
|
+
begin
|
18
|
+
columns
|
19
|
+
rescue ActiveRecord::StatementInvalid # If the table does not exist
|
20
|
+
Array.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def column_names_for_type(*types)
|
25
|
+
safe_columns.select { |column| types.include? column.type }.collect(&:name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def column_names_without_type(*types)
|
29
|
+
safe_columns.select { |column| ! types.include? column.type }.collect(&:name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def boolean_column_names
|
33
|
+
column_names_for_type :boolean
|
34
|
+
end
|
35
|
+
|
36
|
+
def datetime_column_names
|
37
|
+
column_names_for_type :datetime, :date
|
38
|
+
end
|
39
|
+
|
40
|
+
def text_and_string_column_names
|
41
|
+
column_names_for_type :text, :string
|
42
|
+
end
|
43
|
+
|
44
|
+
def non_state_text_and_string_columns
|
45
|
+
text_and_string_column_names.reject { |name| name =~ Pacecar::Helpers.options[:state_pattern] }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Limit
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_limit_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_limit_scopes
|
15
|
+
named_scope :limited, lambda { |*args|
|
16
|
+
{ :limit => args.flatten.first || (defined?(per_page) ? per_page : Pacecar::Helpers.options[:default_limit]) }
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Order
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_order_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_order_scopes
|
15
|
+
safe_columns.collect(&:name).each do |name|
|
16
|
+
named_scope "by_#{name}".to_sym, lambda { |*args|
|
17
|
+
{ :order => "#{quoted_table_name}.#{name} #{args.flatten.first || 'asc'}" }
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Polymorph
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def has_polymorph(name)
|
10
|
+
named_scope "for_#{name}_type".to_sym, lambda { |type|
|
11
|
+
{ :conditions => ["#{quoted_table_name}.#{name}_type = ?", type.to_s] }
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Presence
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_presence_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_presence_scopes
|
15
|
+
column_names_without_type(:boolean).each do |name|
|
16
|
+
named_scope "#{name}_present".to_sym, :conditions => "#{quoted_table_name}.#{name} is not null"
|
17
|
+
named_scope "#{name}_missing".to_sym, :conditions => "#{quoted_table_name}.#{name} is null"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Ranking
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def has_ranking(association)
|
10
|
+
define_ranking_scope association, :maximum, :desc
|
11
|
+
define_ranking_scope association, :minimum, :asc
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def define_ranking_scope(association, name, direction)
|
17
|
+
named_scope "#{name}_#{association}",
|
18
|
+
:joins => "inner join #{association} on #{association}.#{reflections[association].primary_key_name} = #{quoted_table_name}.#{primary_key}",
|
19
|
+
:select => "#{quoted_table_name}.*, count(#{quoted_table_name}.#{primary_key}) as #{association}_count",
|
20
|
+
:group => "#{association}.#{reflections[association].primary_key_name}",
|
21
|
+
:order => "#{association}_count #{direction}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Search
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_search_scopes
|
10
|
+
base.send :define_basic_search_scope
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def define_search_scopes
|
16
|
+
text_and_string_column_names.each do |name|
|
17
|
+
named_scope "#{name}_matches".to_sym, lambda { |query|
|
18
|
+
{ :conditions => ["#{quoted_table_name}.#{name} like :query", { :query => "%#{query}%" }] }
|
19
|
+
}
|
20
|
+
named_scope "#{name}_starts_with".to_sym, lambda { |query|
|
21
|
+
{ :conditions => ["#{quoted_table_name}.#{name} like :query", { :query => "#{query}%" }] }
|
22
|
+
}
|
23
|
+
named_scope "#{name}_ends_with".to_sym, lambda { |query|
|
24
|
+
{ :conditions => ["#{quoted_table_name}.#{name} like :query", { :query => "%#{query}" }] }
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_basic_search_scope
|
30
|
+
named_scope :search_for, lambda { |*args|
|
31
|
+
opts = args.extract_options!
|
32
|
+
query = args.flatten.first
|
33
|
+
columns = opts[:on] || non_state_text_and_string_columns
|
34
|
+
joiner = opts[:require].eql?(:all) ? 'and' : 'or'
|
35
|
+
match = columns.collect { |name| "#{quoted_table_name}.#{name} like :query" }.join(" #{joiner} ")
|
36
|
+
{ :conditions => [match, { :query => "%#{query}%" } ] }
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module State
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def has_state(*names)
|
10
|
+
opts = names.extract_options!
|
11
|
+
names.each do |name|
|
12
|
+
constant = opts[:with] || const_get(name.to_s.pluralize.upcase)
|
13
|
+
constant.each do |state|
|
14
|
+
named_scope "#{name}_#{state.downcase}".to_sym, :conditions => ["#{quoted_table_name}.#{name} = ?", state]
|
15
|
+
named_scope "#{name}_not_#{state.downcase}".to_sym, :conditions => ["#{quoted_table_name}.#{name} <> ?", state]
|
16
|
+
self.class_eval %Q{
|
17
|
+
def #{name}_#{state.downcase}?
|
18
|
+
#{name} == '#{state}'
|
19
|
+
end
|
20
|
+
def #{name}_not_#{state.downcase}?
|
21
|
+
#{name} != '#{state}'
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
named_scope "#{name}".to_sym, lambda { |state|
|
26
|
+
{ :conditions => ["#{quoted_table_name}.#{name} = ?", state] }
|
27
|
+
}
|
28
|
+
named_scope "#{name}_not".to_sym, lambda { |state|
|
29
|
+
{ :conditions => ["#{quoted_table_name}.#{name} <> ?", state] }
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pacecar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.8
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Jankowski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-01 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Generated scopes for ActiveRecord classes.
|
17
|
+
email: mjankowski@thoughtbot.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- init.rb
|
26
|
+
- lib/pacecar/associations.rb
|
27
|
+
- lib/pacecar/boolean.rb
|
28
|
+
- lib/pacecar/datetime.rb
|
29
|
+
- lib/pacecar/duration.rb
|
30
|
+
- lib/pacecar/helpers.rb
|
31
|
+
- lib/pacecar/limit.rb
|
32
|
+
- lib/pacecar/order.rb
|
33
|
+
- lib/pacecar/polymorph.rb
|
34
|
+
- lib/pacecar/presence.rb
|
35
|
+
- lib/pacecar/ranking.rb
|
36
|
+
- lib/pacecar/search.rb
|
37
|
+
- lib/pacecar/state.rb
|
38
|
+
- lib/pacecar.rb
|
39
|
+
- MIT-LICENSE
|
40
|
+
- README.rdoc
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/thoughtbot/pacecar
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.3.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Pacecar adds named_scope methods to ActiveRecord classes via database column introspection.
|
69
|
+
test_files: []
|
70
|
+
|