passiverecord 0.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/History.txt +6 -0
- data/Manifest.txt +12 -0
- data/README.txt +79 -0
- data/Rakefile +19 -0
- data/lib/passive_record.rb +17 -0
- data/lib/passive_record/associations.rb +276 -0
- data/lib/passive_record/base.rb +126 -0
- data/lib/passive_record/schema.rb +25 -0
- data/lib/passive_record/version.rb +9 -0
- data/test/test_associations.rb +96 -0
- data/test/test_base.rb +87 -0
- data/test/test_helper.rb +2 -0
- metadata +79 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
lib/passive_record.rb
|
6
|
+
lib/passive_record/associations.rb
|
7
|
+
lib/passive_record/base.rb
|
8
|
+
lib/passive_record/schema.rb
|
9
|
+
lib/passive_record/version.rb
|
10
|
+
test/test_associations.rb
|
11
|
+
test/test_base.rb
|
12
|
+
test/test_helper.rb
|
data/README.txt
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
= PassiveRecord
|
2
|
+
by Jason L Perry (http://paint.itred.org)
|
3
|
+
|
4
|
+
== DESCRIPTION:
|
5
|
+
|
6
|
+
A replacement for ActiveRecord when you just need to model
|
7
|
+
a few unchanging objects, and don't necessarily need a full blown
|
8
|
+
ActiveRecord class and table in the database.
|
9
|
+
|
10
|
+
== FEATURES:
|
11
|
+
|
12
|
+
* some basic ActiveRecord style finders
|
13
|
+
* dynamic attribute based finders, supporting basic comparisons as well
|
14
|
+
regular expressions and ranges
|
15
|
+
* some integrated ActiveRecord associactions, ie: ActiveRecord#belongs_to(:passive_record)
|
16
|
+
PassiveRecord#has_many(:active_records) (excluding has_many :through)
|
17
|
+
|
18
|
+
== TODOs:
|
19
|
+
|
20
|
+
* has_many :through
|
21
|
+
* extend find options like :conditions, :order
|
22
|
+
* find(:first) (depends on :order being implemented)
|
23
|
+
* better documentation of the code
|
24
|
+
* fix warnings in tests
|
25
|
+
|
26
|
+
== SYNOPSIS:
|
27
|
+
|
28
|
+
class Continent < PassiveRecord::Base
|
29
|
+
has_many :countries # => an ActiveRecord class
|
30
|
+
|
31
|
+
schema :name => String, :size => Integer, :population => Integer
|
32
|
+
|
33
|
+
create :name => "Africa", :size => 30370000, :population => 890000000
|
34
|
+
create :name => "Antarctica", :size => 13720000, :population => 1000
|
35
|
+
create :name => "Australia", :size => 7600000, :population => 20000000
|
36
|
+
create :name => "Eurasia", :size => 53990000, :population => 4510000000
|
37
|
+
create :name => "North America", :size => 24490000, :population => 515000000
|
38
|
+
create :name => "South America", :size => 17840000, :population => 371000000
|
39
|
+
end
|
40
|
+
|
41
|
+
Continent.find(1) # => Africa
|
42
|
+
Continent.find_by_name("Africa") # => Yes, also Africa
|
43
|
+
Continent.find_by_name_and_size(/America/, 17840000) # => South America
|
44
|
+
Continent.find_all_by_population(1000..20000000) # => Antarctica and Australia
|
45
|
+
Continent.find(:all) # => All 6 (though not in any particular order, yet)
|
46
|
+
|
47
|
+
== REQUIREMENTS:
|
48
|
+
|
49
|
+
* activerecord
|
50
|
+
|
51
|
+
== INSTALL:
|
52
|
+
|
53
|
+
* gem install passiverecord
|
54
|
+
* require "passive_record"
|
55
|
+
|
56
|
+
== LICENSE:
|
57
|
+
|
58
|
+
(The MIT License)
|
59
|
+
|
60
|
+
Copyright (c) 2007 Jason L Perry (Paint.itRed)
|
61
|
+
|
62
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
63
|
+
a copy of this software and associated documentation files (the
|
64
|
+
'Software'), to deal in the Software without restriction, including
|
65
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
66
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
67
|
+
permit persons to whom the Software is furnished to do so, subject to
|
68
|
+
the following conditions:
|
69
|
+
|
70
|
+
The above copyright notice and this permission notice shall be
|
71
|
+
included in all copies or substantial portions of the Software.
|
72
|
+
|
73
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
74
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
75
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
76
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
77
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
78
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
79
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + "/lib")
|
2
|
+
require 'rubygems'
|
3
|
+
require 'hoe'
|
4
|
+
|
5
|
+
require 'passive_record/version'
|
6
|
+
|
7
|
+
Hoe.new('passiverecord', PassiveRecord::VERSION::STRING) do |p|
|
8
|
+
p.rubyforge_name = 'paintitred'
|
9
|
+
p.author = 'Jason L Perry'
|
10
|
+
p.email = 'jasper@ambethia.com'
|
11
|
+
p.summary = 'Pacifying overactive records'
|
12
|
+
p.description = 'Pacifying overactive records'
|
13
|
+
p.url = "http://code.itred.org/projects/passive-record"
|
14
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
15
|
+
p.extra_deps = [['activerecord', '>= 1.15.3']]
|
16
|
+
p.remote_rdoc_dir = "passive_record"
|
17
|
+
end
|
18
|
+
|
19
|
+
Hoe.send :define_method, :extra_deps, proc { @extra_deps.reject! { |x| Array(x).first == 'hoe' };@extra_deps}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'active_record'
|
5
|
+
rescue LoadError => e
|
6
|
+
require 'rubygems'
|
7
|
+
require 'active_record'
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'passive_record/base'
|
11
|
+
require 'passive_record/schema'
|
12
|
+
require 'passive_record/associations'
|
13
|
+
|
14
|
+
PassiveRecord::Base.class_eval do
|
15
|
+
include PassiveRecord::Schema
|
16
|
+
include PassiveRecord::Associations
|
17
|
+
end
|
@@ -0,0 +1,276 @@
|
|
1
|
+
# Currently this is a big hack job of code from active record
|
2
|
+
# TODO: Trim this down, and test it thoroughly
|
3
|
+
module PassiveRecord
|
4
|
+
module Associations
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
|
9
|
+
ActiveRecord::Callbacks::CALLBACKS.each do |method|
|
10
|
+
base.class_eval <<-"end_eval"
|
11
|
+
def self.#{method}(*callbacks, &block)
|
12
|
+
callbacks << block if block_given?
|
13
|
+
write_inheritable_array(#{method.to_sym.inspect}, callbacks)
|
14
|
+
end
|
15
|
+
end_eval
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def quoted_id #:nodoc:
|
20
|
+
ActiveRecord::Base.quote_value(self.key)
|
21
|
+
end
|
22
|
+
|
23
|
+
def attribute_present?(attribute)
|
24
|
+
value = self.respond_to?(attribute) ? self.send(attribute) : nil
|
25
|
+
!value.blank? or value == 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_attribute(attribute)
|
29
|
+
self.send(attribute)
|
30
|
+
end
|
31
|
+
|
32
|
+
def new_record?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
module ClassMethods
|
37
|
+
|
38
|
+
def has_many(association_id, options = {}, &extension)
|
39
|
+
reflection = create_has_many_reflection(association_id, options, &extension)
|
40
|
+
|
41
|
+
ActiveRecord::Base.send(:configure_dependency_for_has_many, reflection)
|
42
|
+
|
43
|
+
if options[:through]
|
44
|
+
collection_reader_method(reflection, ActiveRecord::Associations::HasManyThroughAssociation)
|
45
|
+
else
|
46
|
+
add_multiple_associated_save_callbacks(reflection.name)
|
47
|
+
add_association_callbacks(reflection.name, reflection.options)
|
48
|
+
collection_accessor_methods(reflection, ActiveRecord::Associations::HasManyAssociation)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def has_one(association_id, options = {})
|
53
|
+
reflection = create_has_one_reflection(association_id, options)
|
54
|
+
|
55
|
+
module_eval do
|
56
|
+
after_save <<-EOF
|
57
|
+
association = instance_variable_get("@#{reflection.name}")
|
58
|
+
if !association.nil? && (new_record? || association.new_record? || association["#{reflection.primary_key_name}"] != id)
|
59
|
+
association["#{reflection.primary_key_name}"] = id
|
60
|
+
association.save(true)
|
61
|
+
end
|
62
|
+
EOF
|
63
|
+
end
|
64
|
+
|
65
|
+
association_accessor_methods(reflection, ActiveRecord::Associations::HasOneAssociation)
|
66
|
+
association_constructor_method(:build, reflection, ActiveRecord::Associations::HasOneAssociation)
|
67
|
+
association_constructor_method(:create, reflection, ActiveRecord::Associations::HasOneAssociation)
|
68
|
+
|
69
|
+
ActiveRecord::Base.send(:configure_dependency_for_has_one, reflection)
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_has_many_reflection(association_id, options, &extension)
|
73
|
+
options.assert_valid_keys(
|
74
|
+
:class_name, :table_name, :foreign_key,
|
75
|
+
:exclusively_dependent, :dependent,
|
76
|
+
:select, :conditions, :include, :order, :group, :limit, :offset,
|
77
|
+
:as, :through, :source, :source_type,
|
78
|
+
:uniq,
|
79
|
+
:finder_sql, :counter_sql,
|
80
|
+
:before_add, :after_add, :before_remove, :after_remove,
|
81
|
+
:extend
|
82
|
+
)
|
83
|
+
|
84
|
+
options[:extend] = create_extension_module(association_id, extension) if block_given?
|
85
|
+
|
86
|
+
create_reflection(:has_many, association_id, options, self)
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_has_one_reflection(association_id, options)
|
90
|
+
options.assert_valid_keys(
|
91
|
+
:class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as
|
92
|
+
)
|
93
|
+
|
94
|
+
create_reflection(:has_one, association_id, options, self)
|
95
|
+
end
|
96
|
+
|
97
|
+
def create_reflection(macro, name, options, active_record)
|
98
|
+
case macro
|
99
|
+
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
|
100
|
+
reflection = ActiveRecord::Reflection::AssociationReflection.new(macro, name, options, active_record)
|
101
|
+
when :composed_of
|
102
|
+
reflection = AggregateReflection.new(macro, name, options, active_record)
|
103
|
+
end
|
104
|
+
write_inheritable_hash :reflections, name => reflection
|
105
|
+
reflection
|
106
|
+
end
|
107
|
+
|
108
|
+
def add_multiple_associated_save_callbacks(association_name)
|
109
|
+
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
110
|
+
define_method(method_name) do
|
111
|
+
association = instance_variable_get("@#{association_name}")
|
112
|
+
if association.respond_to?(:loaded?)
|
113
|
+
if new_record?
|
114
|
+
association
|
115
|
+
else
|
116
|
+
association.select { |record| record.new_record? }
|
117
|
+
end.each do |record|
|
118
|
+
errors.add "#{association_name}" unless record.valid?
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
validate method_name
|
124
|
+
before_save("@new_record_before_save = new_record?; true")
|
125
|
+
|
126
|
+
after_callback = <<-end_eval
|
127
|
+
association = instance_variable_get("@#{association_name}")
|
128
|
+
|
129
|
+
if association.respond_to?(:loaded?)
|
130
|
+
if @new_record_before_save
|
131
|
+
records_to_save = association
|
132
|
+
else
|
133
|
+
records_to_save = association.select { |record| record.new_record? }
|
134
|
+
end
|
135
|
+
records_to_save.each { |record| association.send(:insert_record, record) }
|
136
|
+
association.send(:construct_sql) # reconstruct the SQL queries now that we know the owner's id
|
137
|
+
end
|
138
|
+
end_eval
|
139
|
+
|
140
|
+
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
141
|
+
after_create(after_callback)
|
142
|
+
after_update(after_callback)
|
143
|
+
end
|
144
|
+
|
145
|
+
def add_association_callbacks(association_name, options)
|
146
|
+
callbacks = %w(before_add after_add before_remove after_remove)
|
147
|
+
callbacks.each do |callback_name|
|
148
|
+
full_callback_name = "#{callback_name}_for_#{association_name}"
|
149
|
+
defined_callbacks = options[callback_name.to_sym]
|
150
|
+
if options.has_key?(callback_name.to_sym)
|
151
|
+
class_inheritable_reader full_callback_name.to_sym
|
152
|
+
write_inheritable_array(full_callback_name.to_sym, [defined_callbacks].flatten)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def collection_accessor_methods(reflection, association_proxy_class)
|
158
|
+
collection_reader_method(reflection, association_proxy_class)
|
159
|
+
|
160
|
+
define_method("#{reflection.name}=") do |new_value|
|
161
|
+
# Loads proxy class instance (defined in collection_reader_method) if not already loaded
|
162
|
+
association = send(reflection.name)
|
163
|
+
association.replace(new_value)
|
164
|
+
association
|
165
|
+
end
|
166
|
+
|
167
|
+
define_method("#{reflection.name.to_s.singularize}_ids") do
|
168
|
+
send(reflection.name).map(&:id)
|
169
|
+
end
|
170
|
+
|
171
|
+
define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
|
172
|
+
ids = (new_value || []).reject { |nid| nid.blank? }
|
173
|
+
send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def collection_reader_method(reflection, association_proxy_class)
|
178
|
+
define_method(reflection.name) do |*params|
|
179
|
+
force_reload = params.first unless params.empty?
|
180
|
+
association = instance_variable_get("@#{reflection.name}")
|
181
|
+
|
182
|
+
unless association.respond_to?(:loaded?)
|
183
|
+
association = association_proxy_class.new(self, reflection)
|
184
|
+
instance_variable_set("@#{reflection.name}", association)
|
185
|
+
end
|
186
|
+
|
187
|
+
association.reload if force_reload
|
188
|
+
|
189
|
+
association
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def association_accessor_methods(reflection, association_proxy_class)
|
194
|
+
define_method(reflection.name) do |*params|
|
195
|
+
force_reload = params.first unless params.empty?
|
196
|
+
association = instance_variable_get("@#{reflection.name}")
|
197
|
+
|
198
|
+
if association.nil? || force_reload
|
199
|
+
association = association_proxy_class.new(self, reflection)
|
200
|
+
retval = association.reload
|
201
|
+
if retval.nil? and association_proxy_class == BelongsToAssociation
|
202
|
+
instance_variable_set("@#{reflection.name}", nil)
|
203
|
+
return nil
|
204
|
+
end
|
205
|
+
instance_variable_set("@#{reflection.name}", association)
|
206
|
+
end
|
207
|
+
|
208
|
+
association.target.nil? ? nil : association
|
209
|
+
end
|
210
|
+
|
211
|
+
define_method("#{reflection.name}=") do |new_value|
|
212
|
+
association = instance_variable_get("@#{reflection.name}")
|
213
|
+
if association.nil?
|
214
|
+
association = association_proxy_class.new(self, reflection)
|
215
|
+
end
|
216
|
+
|
217
|
+
association.replace(new_value)
|
218
|
+
|
219
|
+
unless new_value.nil?
|
220
|
+
instance_variable_set("@#{reflection.name}", association)
|
221
|
+
else
|
222
|
+
instance_variable_set("@#{reflection.name}", nil)
|
223
|
+
return nil
|
224
|
+
end
|
225
|
+
|
226
|
+
association
|
227
|
+
end
|
228
|
+
|
229
|
+
define_method("set_#{reflection.name}_target") do |target|
|
230
|
+
return if target.nil? and association_proxy_class == BelongsToAssociation
|
231
|
+
association = association_proxy_class.new(self, reflection)
|
232
|
+
association.target = target
|
233
|
+
instance_variable_set("@#{reflection.name}", association)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def association_constructor_method(constructor, reflection, association_proxy_class)
|
238
|
+
define_method("#{constructor}_#{reflection.name}") do |*params|
|
239
|
+
attributees = params.first unless params.empty?
|
240
|
+
replace_existing = params[1].nil? ? true : params[1]
|
241
|
+
association = instance_variable_get("@#{reflection.name}")
|
242
|
+
|
243
|
+
if association.nil?
|
244
|
+
association = association_proxy_class.new(self, reflection)
|
245
|
+
instance_variable_set("@#{reflection.name}", association)
|
246
|
+
end
|
247
|
+
|
248
|
+
if association_proxy_class == HasOneAssociation
|
249
|
+
association.send(constructor, attributees, replace_existing)
|
250
|
+
else
|
251
|
+
association.send(constructor, attributees)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def validate(*methods, &block)
|
257
|
+
methods << block if block_given?
|
258
|
+
write_inheritable_set(:validate, methods)
|
259
|
+
end
|
260
|
+
|
261
|
+
def write_inheritable_set(key, methods)
|
262
|
+
existing_methods = read_inheritable_attribute(key) || []
|
263
|
+
write_inheritable_attribute(key, methods | existing_methods)
|
264
|
+
end
|
265
|
+
|
266
|
+
def compute_type(*args)
|
267
|
+
ActiveRecord::Base.send(:compute_type, *args)
|
268
|
+
end
|
269
|
+
|
270
|
+
def reflect_on_association(*args)
|
271
|
+
ActiveRecord::Base.send(:reflect_on_association, *args)
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module PassiveRecord
|
2
|
+
class PassiveRecordError < StandardError; end
|
3
|
+
class RecordNotFound < PassiveRecordError; end
|
4
|
+
|
5
|
+
class Base
|
6
|
+
@@instances = {}
|
7
|
+
|
8
|
+
attr_accessor :attributes, :key
|
9
|
+
|
10
|
+
alias :id :key
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
@attributes = attributes
|
14
|
+
yield self if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](attribute_name)
|
18
|
+
self.attributes[attribute_name]
|
19
|
+
end
|
20
|
+
|
21
|
+
def []=(attribute_name, value)
|
22
|
+
self.attributes[attribute_name] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(comparison_object)
|
26
|
+
comparison_object.equal?(self) ||
|
27
|
+
(comparison_object.instance_of?(self.class) &&
|
28
|
+
comparison_object.id == id &&
|
29
|
+
!comparison_object.new_record?)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
class << self
|
34
|
+
|
35
|
+
def create(*args)
|
36
|
+
attributes = extract_options!(args)
|
37
|
+
key = args.first || @@instances.size+1
|
38
|
+
instance = self.new(attributes)
|
39
|
+
instance.key = key
|
40
|
+
@@instances[key] = instance
|
41
|
+
return key
|
42
|
+
end
|
43
|
+
|
44
|
+
private :create
|
45
|
+
|
46
|
+
def find(*args)
|
47
|
+
options = extract_options!(args)
|
48
|
+
args.first == :all ? find_every : find_from_keys(args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def count
|
52
|
+
find_every.size
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
# def find_initial(options)
|
58
|
+
# end
|
59
|
+
|
60
|
+
def find_every
|
61
|
+
@@instances.select { |key, value| value.is_a?(self) }.map {|its| its.last}
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_from_keys(keys, options = {})
|
65
|
+
|
66
|
+
expects_array = keys.first.kind_of?(Array)
|
67
|
+
return keys.first if expects_array && keys.first.empty?
|
68
|
+
keys = keys.flatten.compact.uniq
|
69
|
+
|
70
|
+
case keys.size
|
71
|
+
when 0
|
72
|
+
raise RecordNotFound, "Couldn't find #{name} without a key"
|
73
|
+
when 1
|
74
|
+
result = find_one(keys.first)
|
75
|
+
expects_array ? [ result ] : result
|
76
|
+
else
|
77
|
+
find_some(keys)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_one(_key)
|
82
|
+
values = @@instances.select { |key, value| _key == key && value.is_a?(self) }.map {|its| its.last}
|
83
|
+
unless values.empty?
|
84
|
+
return values.first
|
85
|
+
else
|
86
|
+
raise RecordNotFound, "Couldn't find #{name} with the given key."
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def find_some(keys)
|
91
|
+
values = @@instances.select { |key, value| (keys).include?(key) && value.is_a?(self) }.map {|its| its.last}
|
92
|
+
end
|
93
|
+
|
94
|
+
def extract_options!(args) #:nodoc:
|
95
|
+
args.last.is_a?(Hash) ? args.pop : {}
|
96
|
+
end
|
97
|
+
|
98
|
+
# TODO: clean this mess up
|
99
|
+
def method_missing(method_id, *arguments)
|
100
|
+
if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(method_id.to_s)
|
101
|
+
expects_array = match.captures.first == 'all_by'
|
102
|
+
attribute_names = match.captures.last.split('_and_')
|
103
|
+
# super unless all_attributes_exists?(attribute_names)
|
104
|
+
attributes = {}
|
105
|
+
attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
|
106
|
+
expressions = []
|
107
|
+
attributes.each_pair { |key, value| expressions << attribute_expression("value.#{key}", value) }
|
108
|
+
results = @@instances.select { |key, value| eval expressions.join(" && ") }.map {|its| its.last}
|
109
|
+
return expects_array ? results : results.pop
|
110
|
+
else
|
111
|
+
super
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def attribute_expression(name, argument)
|
116
|
+
case argument
|
117
|
+
when Regexp then "#{name} =~ /#{argument}/"
|
118
|
+
when Range then "(#{argument}).include?(#{name})"
|
119
|
+
when String then "#{name} == \"#{argument}\""
|
120
|
+
else "#{name} == #{argument}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PassiveRecord
|
2
|
+
module Schema
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
# Define the schema for your model, this also adds attribute accessors for schema columns
|
11
|
+
def schema(definition = {})
|
12
|
+
@@schema = definition
|
13
|
+
definition.keys.each do |attribute_name|
|
14
|
+
define_method("#{attribute_name}") do
|
15
|
+
self[attribute_name]
|
16
|
+
end
|
17
|
+
define_method("#{attribute_name}=") do |new_value|
|
18
|
+
self[attribute_name] = new_value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class Room < PassiveRecord::Base
|
4
|
+
has_many :furniture, :order => :name
|
5
|
+
has_one :light_fixture, :class_name => "Furniture"
|
6
|
+
|
7
|
+
has_many :ins, :class_name => "Door", :foreign_key => "outside_id"
|
8
|
+
has_many :outs, :class_name => "Door", :foreign_key => "inside_id"
|
9
|
+
|
10
|
+
has_many :exits, :through => :outs
|
11
|
+
has_many :entrances, :through => :ins
|
12
|
+
|
13
|
+
schema :name => String
|
14
|
+
|
15
|
+
create :name => "Family Room"
|
16
|
+
create :name => "Kitchen"
|
17
|
+
create :name => "Bedroom"
|
18
|
+
create :name => "Restroom"
|
19
|
+
create :name => "Office"
|
20
|
+
end
|
21
|
+
|
22
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
23
|
+
ActiveRecord::Base.connection.create_table "furniture", :force => true do |t|
|
24
|
+
t.column :name, :string
|
25
|
+
t.column :room_id, :integer
|
26
|
+
end
|
27
|
+
|
28
|
+
ActiveRecord::Base.connection.create_table "doors", :force => true do |t|
|
29
|
+
t.column :inside_id, :integer
|
30
|
+
t.column :outside_id, :integer
|
31
|
+
end
|
32
|
+
|
33
|
+
Inflector.inflections { |inflect| inflect.uncountable %w( furniture )}
|
34
|
+
|
35
|
+
class Furniture < ActiveRecord::Base
|
36
|
+
belongs_to :room
|
37
|
+
end
|
38
|
+
|
39
|
+
class Door < ActiveRecord::Base
|
40
|
+
belongs_to :inside, :class_name => "Room", :foreign_key => "inside_id"
|
41
|
+
belongs_to :outside, :class_name => "Room", :foreign_key => "outside_id"
|
42
|
+
end
|
43
|
+
|
44
|
+
class PassiveRecord::AssociationsTest < Test::Unit::TestCase
|
45
|
+
|
46
|
+
# some "fixtures"
|
47
|
+
def setup
|
48
|
+
Furniture.create :name => "Couch", :room_id => Room.find_by_name("Family Room").id
|
49
|
+
Furniture.create :name => "Ottoman", :room_id => Room.find_by_name("Family Room").id
|
50
|
+
Furniture.create :name => "Ott-lite", :room_id => Room.find_by_name("Office").id
|
51
|
+
|
52
|
+
Door.create :inside_id => Room.find_by_name("Office").id,
|
53
|
+
:outside_id => Room.find_by_name("Family Room").id
|
54
|
+
|
55
|
+
Door.create :inside_id => Room.find_by_name("Restroom").id,
|
56
|
+
:outside_id => Room.find_by_name("Family Room").id
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_should_have_many
|
60
|
+
furniture = [
|
61
|
+
Furniture.find_by_name("Couch"),
|
62
|
+
Furniture.find_by_name("Ottoman")
|
63
|
+
]
|
64
|
+
room = Room.find_by_name("Family Room")
|
65
|
+
assert_equal furniture, room.furniture
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_should_have_many_through
|
69
|
+
rooms = [
|
70
|
+
Room.find_by_name("Office"),
|
71
|
+
Room.find_by_name("Restroom")
|
72
|
+
]
|
73
|
+
|
74
|
+
room = Room.find_by_name("Family Room")
|
75
|
+
|
76
|
+
# assert_equal room, room.exits
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_should_have_one
|
80
|
+
lamp = Furniture.find_by_name("Ott-lite")
|
81
|
+
room = Room.find_by_name("Office")
|
82
|
+
assert_equal lamp, room.light_fixture
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_should_belong_to
|
86
|
+
lamp = Furniture.find_by_name("Ott-lite")
|
87
|
+
room = Room.find_by_name("Office")
|
88
|
+
|
89
|
+
assert_equal room, lamp.room
|
90
|
+
end
|
91
|
+
|
92
|
+
def teardown
|
93
|
+
Furniture.delete_all
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
data/test/test_base.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class Continent < PassiveRecord::Base
|
4
|
+
schema :name => String, :size => Integer, :population => Integer
|
5
|
+
end
|
6
|
+
|
7
|
+
class PassiveRecord::BaseTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
# Add the geographic 6 continents
|
11
|
+
Continent.send :create, :name => "Africa", :size => 30370000, :population => 890000000
|
12
|
+
Continent.send :create, :name => "Antarctica", :size => 13720000, :population => 1000
|
13
|
+
Continent.send :create, :name => "Australia", :size => 7600000, :population => 20000000
|
14
|
+
Continent.send :create, :name => "Eurasia", :size => 53990000, :population => 4510000000
|
15
|
+
Continent.send :create, :name => "North America", :size => 24490000, :population => 515000000
|
16
|
+
Continent.send :create, :name => "South America", :size => 17840000, :population => 371000000
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_should_create_instance
|
20
|
+
assert_equal 7, Continent.send(:create, :name => "Atlantis", :size => 0, :population => 0)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_create_instance_with_manual_key
|
24
|
+
assert_equal "ATL", Continent.send(:create, "ATL", :name => "Atlantis", :size => 0, :population => 0)
|
25
|
+
assert Continent.find("ATL")
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_count_instances
|
29
|
+
assert_equal 6, Continent.count
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_find_one_by_key
|
33
|
+
assert Continent.find(1).is_a?(Continent)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_should_find_one_by_key_as_array
|
37
|
+
assert Continent.find([6]).is_a?(Array)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_should_find_some_by_key
|
41
|
+
assert_equal 2, Continent.find(1,2).size
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_find_some_by_key_as_array
|
45
|
+
assert_equal 2, Continent.find([5,6]).size
|
46
|
+
assert_equal 2, Continent.find([5,6,7]).size
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_should_find_all
|
50
|
+
assert_equal 6, Continent.find(:all).size
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_should_get_attributes
|
54
|
+
assert_equal "Africa", Continent.find(1).name
|
55
|
+
assert_equal 1000, Continent.find(2).population
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_should_set_attributes
|
59
|
+
assert Continent.find(1).name = "Motherland"
|
60
|
+
assert_equal "Motherland", Continent.find(1).name
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_should_find_by_attribute
|
64
|
+
assert_equal Continent.find(1), Continent.find_by_name("Africa")
|
65
|
+
assert_equal Continent.find(5), Continent.find_by_population(515000000)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_should_find_all_by_attribute_as_regex
|
69
|
+
assert_equal Continent.find(5,6), Continent.find_all_by_name(/America/)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_should_find_all_by_attribute_in_range
|
73
|
+
assert_equal Continent.find(2,3), Continent.find_all_by_population(1000..20000000)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_should_find_by_many_attributes
|
77
|
+
assert_equal Continent.find(6), Continent.find_by_name_and_size(/America/, 17840000)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_should_puts_some_stuff
|
81
|
+
end
|
82
|
+
|
83
|
+
def teardown
|
84
|
+
Continent.send :class_variable_set, "@@instances", {}
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: passiverecord
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2007-09-26 00:00:00 -04:00
|
8
|
+
summary: Pacifying overactive records
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: jasper@ambethia.com
|
12
|
+
homepage: http://code.itred.org/projects/passive-record
|
13
|
+
rubyforge_project: paintitred
|
14
|
+
description: Pacifying overactive records
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Jason L Perry
|
31
|
+
files:
|
32
|
+
- History.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
- Rakefile
|
36
|
+
- lib/passive_record.rb
|
37
|
+
- lib/passive_record/associations.rb
|
38
|
+
- lib/passive_record/base.rb
|
39
|
+
- lib/passive_record/schema.rb
|
40
|
+
- lib/passive_record/version.rb
|
41
|
+
- test/test_associations.rb
|
42
|
+
- test/test_base.rb
|
43
|
+
- test/test_helper.rb
|
44
|
+
test_files:
|
45
|
+
- test/test_associations.rb
|
46
|
+
- test/test_base.rb
|
47
|
+
- test/test_helper.rb
|
48
|
+
rdoc_options:
|
49
|
+
- --main
|
50
|
+
- README.txt
|
51
|
+
extra_rdoc_files:
|
52
|
+
- History.txt
|
53
|
+
- Manifest.txt
|
54
|
+
- README.txt
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
dependencies:
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activerecord
|
64
|
+
version_requirement:
|
65
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.15.3
|
70
|
+
version:
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: hoe
|
73
|
+
version_requirement:
|
74
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 1.3.0
|
79
|
+
version:
|