djsun-mongo_mapper 0.5.0.1
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/.gitignore +8 -0
- data/LICENSE +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +87 -0
- data/VERSION +1 -0
- data/bin/mmconsole +55 -0
- data/lib/mongo_mapper/associations/base.rb +83 -0
- data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
- data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +27 -0
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +116 -0
- data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
- data/lib/mongo_mapper/associations/many_embedded_proxy.rb +67 -0
- data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
- data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
- data/lib/mongo_mapper/associations/proxy.rb +74 -0
- data/lib/mongo_mapper/associations.rb +86 -0
- data/lib/mongo_mapper/callbacks.rb +106 -0
- data/lib/mongo_mapper/document.rb +308 -0
- data/lib/mongo_mapper/dynamic_finder.rb +35 -0
- data/lib/mongo_mapper/embedded_document.rb +354 -0
- data/lib/mongo_mapper/finder_options.rb +94 -0
- data/lib/mongo_mapper/key.rb +32 -0
- data/lib/mongo_mapper/observing.rb +50 -0
- data/lib/mongo_mapper/pagination.rb +51 -0
- data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
- data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
- data/lib/mongo_mapper/save_with_validation.rb +19 -0
- data/lib/mongo_mapper/serialization.rb +55 -0
- data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
- data/lib/mongo_mapper/support.rb +171 -0
- data/lib/mongo_mapper/validations.rb +69 -0
- data/lib/mongo_mapper.rb +95 -0
- data/mongo_mapper.gemspec +156 -0
- data/specs.watchr +32 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/custom_matchers.rb +48 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +49 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +244 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +132 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +174 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +297 -0
- data/test/functional/associations/test_many_proxy.rb +331 -0
- data/test/functional/test_associations.rb +44 -0
- data/test/functional/test_binary.rb +18 -0
- data/test/functional/test_callbacks.rb +85 -0
- data/test/functional/test_document.rb +964 -0
- data/test/functional/test_embedded_document.rb +97 -0
- data/test/functional/test_logger.rb +20 -0
- data/test/functional/test_pagination.rb +87 -0
- data/test/functional/test_rails_compatibility.rb +30 -0
- data/test/functional/test_validations.rb +279 -0
- data/test/models.rb +169 -0
- data/test/test_helper.rb +30 -0
- data/test/unit/serializers/test_json_serializer.rb +193 -0
- data/test/unit/test_association_base.rb +144 -0
- data/test/unit/test_document.rb +165 -0
- data/test/unit/test_dynamic_finder.rb +125 -0
- data/test/unit/test_embedded_document.rb +643 -0
- data/test/unit/test_finder_options.rb +193 -0
- data/test/unit/test_key.rb +175 -0
- data/test/unit/test_mongomapper.rb +28 -0
- data/test/unit/test_observing.rb +101 -0
- data/test/unit/test_pagination.rb +109 -0
- data/test/unit/test_rails_compatibility.rb +39 -0
- data/test/unit/test_serializations.rb +52 -0
- data/test/unit/test_support.rb +272 -0
- data/test/unit/test_time_zones.rb +40 -0
- data/test/unit/test_validations.rb +503 -0
- metadata +207 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Associations
|
3
|
+
module ClassMethods
|
4
|
+
def belongs_to(association_id, options = {})
|
5
|
+
create_association(:belongs_to, association_id, options)
|
6
|
+
self
|
7
|
+
end
|
8
|
+
|
9
|
+
def many(association_id, options = {})
|
10
|
+
create_association(:many, association_id, options)
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def associations
|
15
|
+
@associations ||= self.superclass.respond_to?(:associations) ?
|
16
|
+
self.superclass.associations :
|
17
|
+
HashWithIndifferentAccess.new
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def create_association(type, name, options)
|
22
|
+
association = Associations::Base.new(type, name, options)
|
23
|
+
associations[association.name] = association
|
24
|
+
define_association_methods(association)
|
25
|
+
define_dependent_callback(association)
|
26
|
+
association
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_association_methods(association)
|
30
|
+
define_method(association.name) do
|
31
|
+
get_proxy(association)
|
32
|
+
end
|
33
|
+
|
34
|
+
define_method("#{association.name}=") do |value|
|
35
|
+
get_proxy(association).replace(value)
|
36
|
+
value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def define_dependent_callback(association)
|
41
|
+
if association.options[:dependent]
|
42
|
+
if association.many?
|
43
|
+
define_dependent_callback_for_many(association)
|
44
|
+
elsif association.belongs_to?
|
45
|
+
define_dependent_callback_for_belongs_to(association)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_dependent_callback_for_many(association)
|
51
|
+
return if association.embeddable?
|
52
|
+
|
53
|
+
after_destroy do |doc|
|
54
|
+
case association.options[:dependent]
|
55
|
+
when :destroy
|
56
|
+
doc.get_proxy(association).destroy_all
|
57
|
+
when :delete_all
|
58
|
+
doc.get_proxy(association).delete_all
|
59
|
+
when :nullify
|
60
|
+
doc.get_proxy(association).nullify
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def define_dependent_callback_for_belongs_to(association)
|
66
|
+
after_destroy do |doc|
|
67
|
+
case association.options[:dependent]
|
68
|
+
when :destroy
|
69
|
+
doc.get_proxy(association).destroy
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module InstanceMethods
|
76
|
+
def get_proxy(association)
|
77
|
+
unless proxy = self.instance_variable_get(association.ivar)
|
78
|
+
proxy = association.proxy_class.new(self, association)
|
79
|
+
self.instance_variable_set(association.ivar, proxy) if !frozen?
|
80
|
+
end
|
81
|
+
|
82
|
+
proxy
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Callbacks
|
3
|
+
def self.included(model) #:nodoc:
|
4
|
+
model.class_eval do
|
5
|
+
extend Observable
|
6
|
+
include ActiveSupport::Callbacks
|
7
|
+
|
8
|
+
define_callbacks *%w(
|
9
|
+
before_save after_save before_create after_create before_update after_update before_validation
|
10
|
+
after_validation before_validation_on_create after_validation_on_create before_validation_on_update
|
11
|
+
after_validation_on_update before_destroy after_destroy
|
12
|
+
)
|
13
|
+
|
14
|
+
[:create_or_update, :valid?, :create, :update, :destroy].each do |method|
|
15
|
+
alias_method_chain method, :callbacks
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def before_save() end
|
21
|
+
|
22
|
+
def after_save() end
|
23
|
+
def create_or_update_with_callbacks #:nodoc:
|
24
|
+
return false if callback(:before_save) == false
|
25
|
+
if result = create_or_update_without_callbacks
|
26
|
+
callback(:after_save)
|
27
|
+
end
|
28
|
+
result
|
29
|
+
end
|
30
|
+
private :create_or_update_with_callbacks
|
31
|
+
|
32
|
+
def before_create() end
|
33
|
+
|
34
|
+
def after_create() end
|
35
|
+
def create_with_callbacks #:nodoc:
|
36
|
+
return false if callback(:before_create) == false
|
37
|
+
result = create_without_callbacks
|
38
|
+
callback(:after_create)
|
39
|
+
result
|
40
|
+
end
|
41
|
+
private :create_with_callbacks
|
42
|
+
|
43
|
+
def before_update() end
|
44
|
+
|
45
|
+
def after_update() end
|
46
|
+
|
47
|
+
def update_with_callbacks(*args) #:nodoc:
|
48
|
+
return false if callback(:before_update) == false
|
49
|
+
result = update_without_callbacks(*args)
|
50
|
+
callback(:after_update)
|
51
|
+
result
|
52
|
+
end
|
53
|
+
private :update_with_callbacks
|
54
|
+
|
55
|
+
def before_validation() end
|
56
|
+
|
57
|
+
def after_validation() end
|
58
|
+
|
59
|
+
def before_validation_on_create() end
|
60
|
+
|
61
|
+
def after_validation_on_create() end
|
62
|
+
|
63
|
+
def before_validation_on_update() end
|
64
|
+
|
65
|
+
def after_validation_on_update() end
|
66
|
+
|
67
|
+
def valid_with_callbacks? #:nodoc:
|
68
|
+
return false if callback(:before_validation) == false
|
69
|
+
result = new? ? callback(:before_validation_on_create) : callback(:before_validation_on_update)
|
70
|
+
return false if false == result
|
71
|
+
|
72
|
+
result = valid_without_callbacks?
|
73
|
+
callback(:after_validation)
|
74
|
+
|
75
|
+
new? ? callback(:after_validation_on_create) : callback(:after_validation_on_update)
|
76
|
+
return result
|
77
|
+
end
|
78
|
+
|
79
|
+
def before_destroy() end
|
80
|
+
|
81
|
+
def after_destroy() end
|
82
|
+
def destroy_with_callbacks #:nodoc:
|
83
|
+
return false if callback(:before_destroy) == false
|
84
|
+
result = destroy_without_callbacks
|
85
|
+
callback(:after_destroy)
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def callback(method)
|
91
|
+
result = run_callbacks(method) { |result, object| false == result }
|
92
|
+
|
93
|
+
if result != false && respond_to?(method)
|
94
|
+
result = send(method)
|
95
|
+
end
|
96
|
+
|
97
|
+
notify(method)
|
98
|
+
return result
|
99
|
+
end
|
100
|
+
|
101
|
+
def notify(method) #:nodoc:
|
102
|
+
self.class.changed
|
103
|
+
self.class.notify_observers(method, self)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,308 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module MongoMapper
|
4
|
+
module Document
|
5
|
+
def self.included(model)
|
6
|
+
model.class_eval do
|
7
|
+
include EmbeddedDocument
|
8
|
+
include InstanceMethods
|
9
|
+
include Observing
|
10
|
+
include Callbacks
|
11
|
+
include SaveWithValidation
|
12
|
+
include RailsCompatibility::Document
|
13
|
+
extend Validations::Macros
|
14
|
+
extend ClassMethods
|
15
|
+
extend Finders
|
16
|
+
|
17
|
+
def self.per_page
|
18
|
+
25
|
19
|
+
end unless respond_to?(:per_page)
|
20
|
+
end
|
21
|
+
|
22
|
+
descendants << model
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.descendants
|
26
|
+
@descendants ||= Set.new
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def find(*args)
|
31
|
+
options = args.extract_options!
|
32
|
+
case args.first
|
33
|
+
when :first then first(options)
|
34
|
+
when :last then last(options)
|
35
|
+
when :all then find_every(options)
|
36
|
+
when Array then find_some(args, options)
|
37
|
+
else
|
38
|
+
case args.size
|
39
|
+
when 0
|
40
|
+
raise DocumentNotFound, "Couldn't find without an ID"
|
41
|
+
when 1
|
42
|
+
find_one(args[0], options)
|
43
|
+
else
|
44
|
+
find_some(args, options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def paginate(options)
|
50
|
+
per_page = options.delete(:per_page) || self.per_page
|
51
|
+
page = options.delete(:page)
|
52
|
+
total_entries = count(options[:conditions] || {})
|
53
|
+
collection = Pagination::PaginationProxy.new(total_entries, page, per_page)
|
54
|
+
|
55
|
+
options[:limit] = collection.limit
|
56
|
+
options[:skip] = collection.skip
|
57
|
+
|
58
|
+
collection.subject = find_every(options)
|
59
|
+
collection
|
60
|
+
end
|
61
|
+
|
62
|
+
def first(options={})
|
63
|
+
options.merge!(:limit => 1)
|
64
|
+
find_every(options)[0]
|
65
|
+
end
|
66
|
+
|
67
|
+
def last(options={})
|
68
|
+
if options[:order].blank?
|
69
|
+
raise ':order option must be provided when using last'
|
70
|
+
end
|
71
|
+
|
72
|
+
options.merge!(:limit => 1)
|
73
|
+
options[:order] = invert_order_clause(options[:order])
|
74
|
+
find_every(options)[0]
|
75
|
+
end
|
76
|
+
|
77
|
+
def all(options={})
|
78
|
+
find_every(options)
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_by_id(id)
|
82
|
+
criteria = FinderOptions.to_mongo_criteria(:_id => id)
|
83
|
+
if doc = collection.find_one(criteria)
|
84
|
+
new(doc)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def count(conditions={})
|
89
|
+
collection.find(FinderOptions.to_mongo_criteria(conditions)).count
|
90
|
+
end
|
91
|
+
|
92
|
+
def create(*docs)
|
93
|
+
instances = []
|
94
|
+
docs = [{}] if docs.blank?
|
95
|
+
docs.flatten.each do |attrs|
|
96
|
+
doc = new(attrs); doc.save
|
97
|
+
instances << doc
|
98
|
+
end
|
99
|
+
instances.size == 1 ? instances[0] : instances
|
100
|
+
end
|
101
|
+
|
102
|
+
# For updating single document
|
103
|
+
# Person.update(1, {:foo => 'bar'})
|
104
|
+
#
|
105
|
+
# For updating multiple documents at once:
|
106
|
+
# Person.update({'1' => {:foo => 'bar'}, '2' => {:baz => 'wick'}})
|
107
|
+
def update(*args)
|
108
|
+
updating_multiple = args.length == 1
|
109
|
+
if updating_multiple
|
110
|
+
update_multiple(args[0])
|
111
|
+
else
|
112
|
+
id, attributes = args
|
113
|
+
update_single(id, attributes)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def delete(*ids)
|
118
|
+
criteria = FinderOptions.to_mongo_criteria(:_id => ids.flatten)
|
119
|
+
collection.remove(criteria)
|
120
|
+
end
|
121
|
+
|
122
|
+
def delete_all(conditions={})
|
123
|
+
criteria = FinderOptions.to_mongo_criteria(conditions)
|
124
|
+
collection.remove(criteria)
|
125
|
+
end
|
126
|
+
|
127
|
+
def destroy(*ids)
|
128
|
+
find_some(ids.flatten).each(&:destroy)
|
129
|
+
end
|
130
|
+
|
131
|
+
def destroy_all(conditions={})
|
132
|
+
find(:all, :conditions => conditions).each(&:destroy)
|
133
|
+
end
|
134
|
+
|
135
|
+
def connection(mongo_connection=nil)
|
136
|
+
if mongo_connection.nil?
|
137
|
+
@connection ||= MongoMapper.connection
|
138
|
+
else
|
139
|
+
@connection = mongo_connection
|
140
|
+
end
|
141
|
+
@connection
|
142
|
+
end
|
143
|
+
|
144
|
+
def database(name=nil)
|
145
|
+
if name.nil?
|
146
|
+
@database ||= MongoMapper.database
|
147
|
+
else
|
148
|
+
@database = connection.db(name)
|
149
|
+
end
|
150
|
+
@database
|
151
|
+
end
|
152
|
+
|
153
|
+
# Changes the collection name from the default to whatever you want
|
154
|
+
def set_collection_name(name=nil)
|
155
|
+
@collection = nil
|
156
|
+
@collection_name = name
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns the collection name, if not set, defaults to class name tableized
|
160
|
+
def collection_name
|
161
|
+
@collection_name ||= self.to_s.demodulize.tableize
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns the mongo ruby driver collection object
|
165
|
+
def collection
|
166
|
+
@collection ||= database.collection(collection_name)
|
167
|
+
end
|
168
|
+
|
169
|
+
def timestamps!
|
170
|
+
key :created_at, Time
|
171
|
+
key :updated_at, Time
|
172
|
+
|
173
|
+
class_eval { before_save :update_timestamps }
|
174
|
+
end
|
175
|
+
|
176
|
+
protected
|
177
|
+
def method_missing(method, *args)
|
178
|
+
finder = DynamicFinder.new(method)
|
179
|
+
|
180
|
+
if finder.found?
|
181
|
+
meta_def(finder.method) { |*args| dynamic_find(finder, args) }
|
182
|
+
send(finder.method, *args)
|
183
|
+
else
|
184
|
+
super
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
def find_every(options)
|
190
|
+
criteria, options = FinderOptions.new(options).to_a
|
191
|
+
collection.find(criteria, options).to_a.map { |doc| new(doc) }
|
192
|
+
end
|
193
|
+
|
194
|
+
def invert_order_clause(order)
|
195
|
+
order.split(',').map do |order_segment|
|
196
|
+
if order_segment =~ /\sasc/i
|
197
|
+
order_segment.sub /\sasc/i, ' desc'
|
198
|
+
elsif order_segment =~ /\sdesc/i
|
199
|
+
order_segment.sub /\sdesc/i, ' asc'
|
200
|
+
else
|
201
|
+
"#{order_segment.strip} desc"
|
202
|
+
end
|
203
|
+
end.join(',')
|
204
|
+
end
|
205
|
+
|
206
|
+
def find_some(ids, options={})
|
207
|
+
ids = ids.flatten.compact.uniq
|
208
|
+
documents = find_every(options.deep_merge(:conditions => {'_id' => ids}))
|
209
|
+
|
210
|
+
if ids.size == documents.size
|
211
|
+
documents
|
212
|
+
else
|
213
|
+
raise DocumentNotFound, "Couldn't find all of the ids (#{ids.to_sentence}). Found #{documents.size}, but was expecting #{ids.size}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def find_one(id, options={})
|
218
|
+
if doc = find_every(options.deep_merge(:conditions => {:_id => id})).first
|
219
|
+
doc
|
220
|
+
else
|
221
|
+
raise DocumentNotFound, "Document with id of #{id} does not exist in collection named #{collection.name}"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def update_single(id, attrs)
|
226
|
+
if id.blank? || attrs.blank? || !attrs.is_a?(Hash)
|
227
|
+
raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
|
228
|
+
end
|
229
|
+
|
230
|
+
doc = find(id)
|
231
|
+
doc.update_attributes(attrs)
|
232
|
+
doc
|
233
|
+
end
|
234
|
+
|
235
|
+
def update_multiple(docs)
|
236
|
+
unless docs.is_a?(Hash)
|
237
|
+
raise ArgumentError, "Updating multiple documents takes 1 argument and it must be hash"
|
238
|
+
end
|
239
|
+
|
240
|
+
instances = []
|
241
|
+
docs.each_pair { |id, attrs| instances << update(id, attrs) }
|
242
|
+
instances
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
module InstanceMethods
|
247
|
+
def collection
|
248
|
+
self.class.collection
|
249
|
+
end
|
250
|
+
|
251
|
+
def new?
|
252
|
+
read_attribute('_id').blank? || using_custom_id?
|
253
|
+
end
|
254
|
+
|
255
|
+
def save
|
256
|
+
create_or_update
|
257
|
+
end
|
258
|
+
|
259
|
+
def save!
|
260
|
+
create_or_update || raise(DocumentNotValid.new(self))
|
261
|
+
end
|
262
|
+
|
263
|
+
def destroy
|
264
|
+
return false if frozen?
|
265
|
+
|
266
|
+
criteria = FinderOptions.to_mongo_criteria(:_id => id)
|
267
|
+
collection.remove(criteria) unless new?
|
268
|
+
freeze
|
269
|
+
end
|
270
|
+
|
271
|
+
private
|
272
|
+
def create_or_update
|
273
|
+
result = new? ? create : update
|
274
|
+
result != false
|
275
|
+
end
|
276
|
+
|
277
|
+
def create
|
278
|
+
assign_id
|
279
|
+
save_to_collection
|
280
|
+
end
|
281
|
+
|
282
|
+
def assign_id
|
283
|
+
if read_attribute(:_id).blank?
|
284
|
+
write_attribute(:_id, Mongo::ObjectID.new.to_s)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def update
|
289
|
+
save_to_collection
|
290
|
+
end
|
291
|
+
|
292
|
+
def save_to_collection
|
293
|
+
clear_custom_id_flag
|
294
|
+
collection.save(to_mongo)
|
295
|
+
end
|
296
|
+
|
297
|
+
def update_timestamps
|
298
|
+
now = Time.now.utc
|
299
|
+
write_attribute('created_at', now) if new?
|
300
|
+
write_attribute('updated_at', now)
|
301
|
+
end
|
302
|
+
|
303
|
+
def clear_custom_id_flag
|
304
|
+
@using_custom_id = nil
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end # Document
|
308
|
+
end # MongoMapper
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
class DynamicFinder
|
3
|
+
attr_reader :method, :attributes, :finder, :bang, :instantiator
|
4
|
+
|
5
|
+
def initialize(method)
|
6
|
+
@method = method
|
7
|
+
@finder = :first
|
8
|
+
@bang = false
|
9
|
+
match()
|
10
|
+
end
|
11
|
+
|
12
|
+
def found?
|
13
|
+
@finder.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
def match
|
18
|
+
case method.to_s
|
19
|
+
when /^find_(all_by|by)_([_a-zA-Z]\w*)$/
|
20
|
+
@finder = :all if $1 == 'all_by'
|
21
|
+
names = $2
|
22
|
+
when /^find_by_([_a-zA-Z]\w*)\!$/
|
23
|
+
@bang = true
|
24
|
+
names = $1
|
25
|
+
when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
|
26
|
+
@instantiator = $1 == 'initialize' ? :new : :create
|
27
|
+
names = $2
|
28
|
+
else
|
29
|
+
@finder = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
@attributes = names && names.split('_and_')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|