bomberstudios-stone 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,55 @@
1
+ require 'fileutils'
2
+ require 'rubygems'
3
+ require 'validatable'
4
+ require 'english/inflect'
5
+ require 'yaml'
6
+
7
+ require File.expand_path(File.dirname(__FILE__) + '/stone/core_ext/enumerable')
8
+ require File.expand_path(File.dirname(__FILE__) + '/stone/core_ext/string')
9
+ require File.expand_path(File.dirname(__FILE__) + '/stone/core_ext/symbol')
10
+ require File.expand_path(File.dirname(__FILE__) + '/stone/core_ext/datetime')
11
+ require File.expand_path(File.dirname(__FILE__) + '/stone/query')
12
+ require File.expand_path(File.dirname(__FILE__) + '/stone/data_store')
13
+ require File.expand_path(File.dirname(__FILE__) + '/stone/callbacks')
14
+ require File.expand_path(File.dirname(__FILE__) + '/stone/resource')
15
+
16
+ STONE_ROOT = Dir.pwd
17
+
18
+ module Stone
19
+ class << self
20
+
21
+ # For spec stuff only
22
+ def empty_datastore
23
+ if File.exists? STONE_ROOT/"sandbox_for_specs/datastore"
24
+ FileUtils.rm_rf STONE_ROOT/"sandbox_for_specs/datastore"
25
+ end
26
+ end
27
+
28
+ # Creates or updates a datastore at +path+
29
+ # === Parameters
30
+ # +path+<String>::
31
+ # Path to create or update datastore (usually an application's root)
32
+ # +resources+<Array>:: A list of resources that exist for the application
33
+ def start(path, resources, framework = nil)
34
+ DataStore.local_dir = path/"datastore"
35
+
36
+ # create the datastore dir unless it exists
37
+ FileUtils.mkdir_p(DataStore.local_dir) unless File.exists?(DataStore.local_dir)
38
+
39
+ # create a .stone_metadata that contains the resource locations
40
+ # for Stone::Utilities to use
41
+ File.open(DataStore.local_dir/".stone_metadata", "w") do |out|
42
+ YAML.dump({:rsrc_path => File.dirname(resources.first)}, out)
43
+ end unless File.exists?(DataStore.local_dir/".stone_metadata")
44
+
45
+ # load each resource unless a framework has already done it
46
+ resources.each do |resource|
47
+ require resource unless framework == :merb || framework == :rails
48
+ name = File.basename(resource,".rb").pluralize
49
+ unless File.exists? DataStore.local_dir/name
50
+ FileUtils.mkdir_p(DataStore.local_dir/name)
51
+ end
52
+ end
53
+ end
54
+ end # self
55
+ end # Stone
@@ -0,0 +1,50 @@
1
+ module Stone
2
+ class Callbacks < Hash
3
+
4
+ CALLBACKS = [
5
+ :before_save,
6
+ :after_save,
7
+ :before_create,
8
+ :after_create,
9
+ :before_destroy,
10
+ :after_destroy
11
+ ]
12
+
13
+ class << self
14
+
15
+ end # self
16
+
17
+ # Registers the +klass+ with the current instance of Callbacks in Resource
18
+ # === Parameters
19
+ # +klass+:: The class to be registered
20
+ def register_klass(klass)
21
+ self[klass.to_s.make_key] = {}
22
+ CALLBACKS.each do |cb_sym|
23
+ self[klass.to_s.make_key][cb_sym] = []
24
+ end
25
+ end
26
+
27
+ # Adds a given +meth+ to the +cb_sym+ array in the current Callbacks
28
+ # hash based on +klass+
29
+ # === Parameters
30
+ # +cb_sym+:: where +meth+ will be added
31
+ # +meth+:: method to be added
32
+ # +klass+:: determines which +cb_sym+ array will be used
33
+ def register(cb_sym, meth, klass)
34
+ self[klass.to_s.make_key][cb_sym] << meth
35
+ end
36
+
37
+ # Sends any methods registered under +cb_sym+ to +obj+
38
+ # === Parameters
39
+ # +cb_sym+:: Used to retrieve the methods to send
40
+ # +obj+:: The object to which the retrieved methods are sent
41
+ def fire(cb_sym, obj)
42
+ unless obj.model == :class
43
+ self[obj.model][cb_sym].each do |meth|
44
+ obj.send(meth) unless meth.blank?
45
+ end
46
+ end
47
+ true
48
+ end
49
+ end # Callbacks
50
+ end # Stone
@@ -0,0 +1,18 @@
1
+ # Fixes the bug with YAML and DateTime, where
2
+ # YAML::load(DateTime.now.to_yaml).class == Time
3
+ require "yaml"
4
+ class DateTime
5
+ yaml_as "tag:ruby.yaml.org,2002:datetime"
6
+ def DateTime.yaml_new(klass, tag, val)
7
+ if String === val
8
+ self.parse(val)
9
+ else
10
+ raise YAML::TypeError, "Invalid DateTime: " + val.inspect
11
+ end
12
+ end
13
+ def to_yaml( opts = {} )
14
+ YAML::quick_emit( object_id, opts ) do |out|
15
+ out.scalar( taguri, self.to_s, :plain )
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ module Enumerable
2
+ def blank?
3
+ return self.size <= 0
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ class String #:nodoc:
2
+ def /(o)
3
+ File.join(self, o.to_s)
4
+ end
5
+ def make_key
6
+ self.downcase.to_sym
7
+ end
8
+ # From Ruby Facets
9
+ def titlecase
10
+ gsub(/\b\w/){$&.upcase}
11
+ end
12
+ end
@@ -0,0 +1,25 @@
1
+ class Symbol
2
+ def gt; Stone::Query.new(self.to_s, :gt); end
3
+ def gte; Stone::Query.new(self.to_s, :gte); end
4
+ def lt; Stone::Query.new(self.to_s, :lt); end
5
+ def lte; Stone::Query.new(self.to_s, :lte); end
6
+
7
+ def includes
8
+ Stone::Query.new(self.to_s, :includes)
9
+ end
10
+
11
+ def matches
12
+ Stone::Query.new(self.to_s, :matches)
13
+ end
14
+
15
+ def equals
16
+ Stone::Query.new(self.to_s, :equals)
17
+ end
18
+
19
+ def not
20
+ Stone::Query.new(self.to_s, :not)
21
+ end
22
+ def blank?
23
+ return self.to_s.size <= 0
24
+ end
25
+ end
@@ -0,0 +1,70 @@
1
+ module Stone
2
+ # An in-memory representation of the file-based datastore
3
+ class DataStore
4
+
5
+ class << self
6
+ attr_accessor :local_dir
7
+
8
+ # Loads yaml files specific to the resource represented by +sym+
9
+ # === Parameters
10
+ # +sym+::
11
+ # Symbol representing resource data to load
12
+ def load_data(sym)
13
+ ymls = Dir.glob(self.local_dir/sym.to_s.pluralize/"*.yml").sort { |a,b| File.basename(a,".yml").to_i - File.basename(b,".yml").to_i }
14
+ objs = []
15
+ unless ymls.empty?
16
+ ymls.each do |yml|
17
+ obj = YAML.load_file yml
18
+ objs << [obj.id, obj]
19
+ end
20
+ end
21
+ return objs
22
+ end
23
+
24
+ # If the object already exists (id was found),
25
+ # this returns +put+(update), else +post+(create)
26
+ # === Parameters
27
+ # +obj+::
28
+ # The instantiated resource to be saved
29
+ # +store+::
30
+ # DataStore object
31
+ def determine_save_method(obj, store)
32
+ store.resources[obj.model].each do |o|
33
+ return :put if o[0] == obj.id
34
+ end
35
+ :post
36
+ end
37
+
38
+ # Persist the object via YAML
39
+ # === Parameters
40
+ # +obj+:: The object to be persisted
41
+ def write_yaml(obj)
42
+ path = self.local_dir/obj.models/"#{obj.id}.yml"
43
+ File.open(path, 'w') do |out|
44
+ YAML.dump(obj, out)
45
+ end
46
+ end
47
+
48
+ # Removes object's yaml file
49
+ # === Parameters
50
+ # +id+:: id of the object to be removed
51
+ # +klass_dir+:: directory in which object resides
52
+ def delete(id, klass_dir)
53
+ raise "Object could not be found" \
54
+ unless File.exists?(self.local_dir/klass_dir/"#{id}.yml")
55
+
56
+ FileUtils.remove_file(self.local_dir/klass_dir/"#{id}.yml")
57
+ true
58
+ end
59
+ end # self
60
+
61
+ def initialize
62
+ @resources = {}
63
+ end
64
+
65
+ def resources
66
+ @resources
67
+ end
68
+
69
+ end # DataStore
70
+ end # Stone
@@ -0,0 +1,44 @@
1
+ module Stone
2
+ # Represents a single query condition
3
+ class Query
4
+
5
+ attr_accessor :field, :op
6
+
7
+ def initialize(field,op)
8
+ self.field = field
9
+ self.op = case op
10
+ when :gt
11
+ ".>"
12
+ when :lt
13
+ ".<"
14
+ when :gte
15
+ ".>="
16
+ when :lte
17
+ ".<="
18
+ when :includes
19
+ ".include?"
20
+ when :matches
21
+ ".=~"
22
+ when :equals
23
+ ".=="
24
+ when :not
25
+ ".!="
26
+ end
27
+ end
28
+
29
+ # Builds an expression from the conditions given during first or all
30
+ # === Parameters
31
+ # +arg+:: Some conditional argument
32
+ def expression_for(arg)
33
+ if arg.is_a? String
34
+ "#{self.field}#{self.op}('#{arg}')"
35
+ elsif arg.is_a? Regexp
36
+ "#{self.field}#{self.op}(#{arg.inspect})"
37
+ elsif arg.is_a? Date
38
+ "#{self.field}#{self.op}(DateTime.parse('#{arg}'))"
39
+ else
40
+ "#{self.field}#{self.op}(#{arg})"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,472 @@
1
+ module Stone
2
+ # Adds the ability to persist any class it is included in
3
+ # === Example
4
+ #
5
+ # class Post
6
+ # include Stone::Resource
7
+ #
8
+ # field :body, String
9
+ # end
10
+ #
11
+ module Resource
12
+
13
+ attr_accessor :id
14
+
15
+ class << self
16
+ def included(base)
17
+ rsrc_sym = base.to_s.make_key
18
+
19
+ @@callbacks ||= Callbacks.new
20
+ @@callbacks.register_klass(base)
21
+
22
+ @@store ||= DataStore.new
23
+
24
+ base.send(:extend, self)
25
+ base.send(:include, ::Validatable)
26
+
27
+ # rspec breaks if its classes have their contructor overwritten
28
+ unless base.to_s.downcase =~ /(spec::example::examplegroup::subclass_\d|blah)/
29
+ # allow object to be created with a hash of attributes...
30
+ # [] allows for obj[attribute] retrieval
31
+ # to_s allows for stupid Rails to work
32
+ base.class_eval <<-EOS, __FILE__, __LINE__
33
+ def initialize(hash = {})
34
+ self.id = self.next_id_for_klass(self.class)
35
+ unless hash.blank?
36
+ update_attributes(hash)
37
+ end
38
+ @@fields[self.model].each do |f|
39
+ instance_variable_set("@"+f[:name].to_s,[]) if f[:klass] == Array
40
+ end
41
+ end
42
+
43
+ def to_s
44
+ id
45
+ end
46
+
47
+ def [](sym)
48
+ self.send(sym)
49
+ end
50
+ EOS
51
+ end
52
+
53
+ unless @@store.resources.include?(rsrc_sym)
54
+ @@store.resources[rsrc_sym] = DataStore.load_data(rsrc_sym)
55
+ end
56
+ end
57
+ end # self
58
+
59
+ @@fields = {}
60
+
61
+ # Adds a given field to @@fields and inserts an accessor for that
62
+ # field into klass
63
+ # === Parameters
64
+ # +name+<String>::
65
+ #
66
+ def field(name, klass, arg = nil)
67
+ if arg && arg[:unique] == true
68
+ unique = true
69
+ else
70
+ unique = false
71
+ end
72
+ klass_sym = self.to_s.make_key
73
+ unless @@fields[klass_sym]
74
+ @@fields[klass_sym] = [{:name => name,
75
+ :klass => klass,
76
+ :unique => unique}]
77
+ else
78
+ @@fields[klass_sym] << {:name => name,
79
+ :klass => klass,
80
+ :unique => unique}
81
+ end
82
+ name = name.to_s
83
+ # add accessor for given field
84
+ unless klass == Array
85
+ self.class_eval <<-EOS, __FILE__, __LINE__
86
+ def #{name}
87
+ @#{name}
88
+ end
89
+ def #{name}=(value)
90
+ @#{name} = value
91
+ end
92
+ EOS
93
+ else
94
+ self.class_eval <<-EOS, __FILE__, __LINE__
95
+ def #{name}
96
+ @#{name}
97
+ end
98
+ EOS
99
+ end
100
+ self.send(:define_method,"next_by_#{name}") {
101
+ self.next(name.to_sym)
102
+ }
103
+ self.send(:define_method,"prev_by_#{name}") {
104
+ self.prev(name.to_sym)
105
+ }
106
+ end # field
107
+
108
+ def model
109
+ self.class.to_s.downcase.to_sym
110
+ end
111
+ def models
112
+ self.class.to_s.downcase.pluralize
113
+ end
114
+
115
+ # Registers the given method with the current instance of Callbacks. Upon
116
+ # activation (in this case, right before Resource.save is executed), the
117
+ # +meth+ given is called against the object being, in this case, saved.
118
+ #
119
+ # Note that there is no before_validation callback, because Stone
120
+ # before_save triggers before validations occur anyway
121
+ # === Parameters
122
+ # +meth+:: The method to be registered
123
+ def before_save(meth)
124
+ @@callbacks.register(:before_save, meth, self)
125
+ end
126
+
127
+ # See before_save
128
+ def after_save(meth)
129
+ @@callbacks.register(:after_save, meth, self)
130
+ end
131
+
132
+ # See before_save
133
+ def before_create(meth)
134
+ @@callbacks.register(:before_create, meth, self)
135
+ end
136
+
137
+ # See before_save
138
+ def after_create(meth)
139
+ @@callbacks.register(:after_create, meth, self)
140
+ end
141
+
142
+ # See before_save
143
+ def before_delete(meth)
144
+ @@callbacks.register(:before_delete, meth, self)
145
+ end
146
+
147
+ # See before_save
148
+ def after_delete(meth)
149
+ @@callbacks.register(:after_delete, meth, self)
150
+ end
151
+
152
+ # Registers a one-to-many relationship for +resource+
153
+ # === Parameters
154
+ # +resource+::
155
+ # the resource of which this class has many
156
+ def has_many(resource, *args)
157
+ self.class_eval <<-EOS, __FILE__, __LINE__
158
+ def #{resource.to_s}
159
+ #{resource.to_s.singularize.titlecase}.all("#{self.to_s.downcase}_id".to_sym.equals => self.id)
160
+ end
161
+ EOS
162
+ end
163
+
164
+ # Registers a one-to-one relationship for +resource+
165
+ # === Parameters
166
+ # +resource+::
167
+ # the resource of which this class has one
168
+ def has_one(resource, *args)
169
+ field "#{resource.to_s}_id".to_sym, Fixnum
170
+ end
171
+
172
+ # Registers a belongs_to association for +resource+
173
+ # === Parameters
174
+ # +resource+ :: The resource to which this class belongs
175
+ def belongs_to(resource, *args)
176
+ field "#{resource.to_s}_id".to_sym, Fixnum
177
+ self.class_eval <<-EOS, __FILE__, __LINE__
178
+ def #{resource.to_s}
179
+ #{resource.to_s.titlecase}[self.#{resource.to_s}_id]
180
+ end
181
+ EOS
182
+ end
183
+
184
+ # Registers a many-to-many association using an array of +resource+
185
+ # ids.
186
+ # === Parameters
187
+ # +resource+::
188
+ # The resource of which this class belongs to and has many
189
+ def has_and_belongs_to_many(resource, *args)
190
+ field resource, Array
191
+ end
192
+
193
+ alias_method :habtm, :has_and_belongs_to_many
194
+
195
+ # Returns the first object matching +conditions+, or the first object
196
+ # if no conditions are specified
197
+ # === Parameters
198
+ # +conditions+::
199
+ # A hash representing one or more Ruby expressions
200
+ def first(conditions = nil)
201
+ unless conditions
202
+ return @@store.resources[self.to_s.make_key].first[1]
203
+ else
204
+ return find(conditions, self.to_s.make_key)[0]
205
+ end
206
+ end
207
+
208
+ # Returns all objects matching +conditions+, or all objects if no
209
+ # conditions are specified
210
+ # === Parameters
211
+ # +conditions+::
212
+ # A hash representing one or more Ruby expressions
213
+ def all(conditions = nil)
214
+ objs = []
215
+ # check for order conditions, make conditions nil if an order condition
216
+ # is the only one passed to Resource.all
217
+ if conditions && conditions[:order]
218
+ order = conditions[:order].to_a.flatten
219
+ conditions.delete(:order)
220
+ conditions = nil if conditions.empty?
221
+ end
222
+ # Don't bother getting into crazy object searches if there are no
223
+ # conditions -- just grab the objects directly out of the current
224
+ # store
225
+ unless conditions
226
+ @@store.resources[self.to_s.make_key].each do |o|
227
+ objs << o[1]
228
+ end
229
+ else
230
+ objs = find(conditions, self.to_s.make_key)
231
+ end
232
+ if order
233
+ raise "Order should be passed with :asc or :desc, got #{order[1].inspect}" \
234
+ unless [:asc,:desc].include? order[1]
235
+ # FIXME: something about this breaks under certain conditions
236
+ # that I can't find yet
237
+ begin
238
+ objs.sort! {|x,y| x.send(order[0]) <=> y.send(order[0])}
239
+ rescue
240
+ end
241
+ objs.reverse! if order[1] == :desc
242
+ end
243
+ objs
244
+ end
245
+
246
+ # Synonymous for get
247
+ # === Parameters
248
+ # +id+:: id of the object to retrieve
249
+ def [](id)
250
+ raise "Expected Fixnum, got #{id.class} for #{self.to_s}[]" \
251
+ unless id.class == Fixnum || id.to_i
252
+ get(id)
253
+ end
254
+
255
+ def fields
256
+ @@fields
257
+ end
258
+
259
+ # Deletes the object with +id+ from the current DataStore instance and
260
+ # its corresponding yaml file
261
+ def delete(id)
262
+ fire(:before_delete)
263
+ DataStore.delete(id, self.to_s.downcase.pluralize)
264
+ @@store.resources[self.to_s.make_key].each_with_index do |o,i|
265
+ @@store.resources[self.to_s.make_key].delete_at(i) if o[0] == id
266
+ end
267
+ fire(:after_delete)
268
+ true
269
+ end
270
+
271
+ # Allow for retrieval of an object in the current DataStore instance by id
272
+ # === Parameters
273
+ # +id+:: id of the object to retrieve
274
+ def get(id)
275
+ id = id.to_i
276
+ raise "Expected Fixnum, got #{id.class} for #{self.to_s}.get" \
277
+ unless id.class == Fixnum
278
+ @@store.resources[self.to_s.make_key].each do |o|
279
+ return o[1] if o[0] == id
280
+ end
281
+ nil
282
+ end
283
+ alias_method :find_by_id, :get
284
+
285
+ # Puts the attribute changes in +hash+
286
+ # === Parameters
287
+ # +hash+:: the attributes to change
288
+ def update_attributes(hash)
289
+ hash.each_key do |k|
290
+ k = k.to_sym
291
+ if hash[k].is_a? Hash
292
+ update_attributes(hash[k])
293
+ else
294
+ self.send(k.to_s+"=", hash[k]) if field_declared?(k,self.class)
295
+ end
296
+ end
297
+ self.save
298
+ end
299
+
300
+ # Determine the next id number to use based on the last stored object's id
301
+ # of class +klass+
302
+ # === Parameters
303
+ # +klass+:: The class of the object to be saved
304
+ def next_id_for_klass(klass)
305
+ sym = klass.to_s.make_key
306
+ if @@store.resources.has_key?(sym) && !@@store.resources[sym].blank?
307
+ return @@store.resources[sym].last[0] + 1
308
+ else
309
+ return 1
310
+ end
311
+ end
312
+
313
+ # Save an object to the current DataStore instance.
314
+ def save
315
+ return false unless self.fields_are_valid?
316
+ fire(:before_save)
317
+ return false unless self.valid?
318
+ sym = DataStore.determine_save_method(self, @@store)
319
+ self.class.send(sym, self)
320
+ fire(:after_save)
321
+ end
322
+
323
+ # Determines whether the field classes of a given object match the field
324
+ # class declarations
325
+ def fields_are_valid?
326
+ klass_sym = self.model
327
+ @@fields[klass_sym].each do |field|
328
+ unless self.send(field[:name]).class == field[:klass] || self.send(field[:name]) == nil || self.already_exists?
329
+ return false
330
+ end
331
+ if field[:unique] == true
332
+ return false if self.class.first(field[:name] => self.send(field[:name])) && !self.already_exists?
333
+ end
334
+ end
335
+ true
336
+ end
337
+
338
+ # Needed for Rails to work
339
+ def new_record?
340
+ !already_exists?
341
+ end
342
+
343
+ # Finds out if the object is already in the current DataStore instance
344
+ def already_exists?
345
+ DataStore.determine_save_method(self, @@store) == :put
346
+ end
347
+
348
+ def next(order_by = :id)
349
+ self.class.all(order_by.gt => self.send(order_by), :order => {order_by => :desc})[0]
350
+ end
351
+ def prev(order_by = :id)
352
+ self.class.all(order_by.lt => self.send(order_by), :order => {order_by => :desc})[0]
353
+ end
354
+
355
+ private
356
+
357
+ # Fires the given callback in the current instance of Callbacks
358
+ # === Parameters
359
+ # +cb_sym+:: The symbol for the callback (e.g. :before_save)
360
+ def fire(cb_sym)
361
+ @@callbacks.fire(cb_sym, self)
362
+ true
363
+ end
364
+
365
+ # Creates a yaml file for +obj+ and adds +obj+ to the current DataStore
366
+ # instance
367
+ # === Parameters
368
+ # +obj+:: The object to be saved
369
+ def post(obj)
370
+ fire(:before_create)
371
+ obj.created_at = DateTime.now if field_declared?(:created_at,obj.class)
372
+ obj.updated_at = DateTime.now if field_declared?(:updated_at,obj.class)
373
+ DataStore.write_yaml(obj)
374
+ @@store.resources[obj.model] << [obj.id, obj]
375
+ fire(:after_create)
376
+ end
377
+
378
+ # Updates the yaml file for +obj+ and overwrites the old object in the
379
+ # the current DataStore instance
380
+ # === Parameters
381
+ #
382
+ def put(obj)
383
+ obj.updated_at = DateTime.now if field_declared?(:updated_at,obj.class)
384
+ DataStore.write_yaml(obj)
385
+ @@store.resources[obj.model].each do |o|
386
+ o[1] = obj if o[0] == obj.id
387
+ end
388
+ true
389
+ end
390
+
391
+ # Find an object according to +conditions+ provided
392
+ # === Parameters
393
+ # +conditions+:: A plain string representation of a set of conditions
394
+ # +key+::
395
+ # A symbol representing the class of objects to look for in the current
396
+ # DataStore instance
397
+ def find(conditions, key) #:doc:
398
+ objs = []
399
+
400
+ if conditions.is_a? Hash
401
+ unless conditions.to_a.flatten.map {|e| e.is_a? Query}.include?(true)
402
+ conds = conditions.to_a.flatten
403
+ @@store.resources[key].each do |o|
404
+ objs << o[1] if o[1].send(conds[0]) == conds[1]
405
+ end
406
+ else
407
+ parsed_conditions = parse_conditions(conditions)
408
+ @@store.resources[key].each do |o|
409
+ objs << o[1] if matches_conditions?(o[1], parsed_conditions)
410
+ end
411
+ end
412
+ else
413
+ raise "Resource.find expects a Hash, got a #{conditions.class}"
414
+ end
415
+ objs
416
+ end
417
+
418
+ # Checks the list of fields for a given +klass+ to see if +field+
419
+ # is included
420
+ # === Parameters
421
+ # +field+:: The field to look for
422
+ # +klass+:: The class to look in
423
+ def field_declared?(field,klass)
424
+ @@fields[klass.to_s.make_key].each do |f|
425
+ return true if f[:name] == field
426
+ end
427
+ false
428
+ end
429
+
430
+ # Executes and evaluates the expressions in +conds+ against
431
+ # the +obj+ provided, and then evaluates those results against
432
+ # the conditionals ("&&") in +conds+
433
+ # === Parameters
434
+ # +obj+:: The object to compare against
435
+ # +conds+::
436
+ # A set of expressions (name == 'nick') and their conditionals
437
+ # ('&&')
438
+ def matches_conditions?(obj, conds) #:doc:
439
+ tf_ary = []
440
+ conds.each_with_index do |cond,i|
441
+ # build an array like [true, "&&", false, "&&", true]
442
+ if i % 2 == 0
443
+ begin
444
+ bool = obj.instance_eval(cond)
445
+ bool = false unless bool
446
+ bool = true if bool.class == Fixnum
447
+ tf_ary << bool
448
+ rescue
449
+ tf_ary << false
450
+ end
451
+ else
452
+ tf_ary << cond
453
+ end
454
+ end
455
+ # evaluate the true/false array
456
+ eval(tf_ary.join)
457
+ end
458
+
459
+ # Turns conditions into a set of expressions that can be evaluated
460
+ def parse_conditions(hash)
461
+ conds = []
462
+ hash.each do |k,v|
463
+ conds << k.expression_for(v)
464
+ conds << "&&"
465
+ end
466
+ conds.pop
467
+ conds
468
+ end
469
+
470
+ end # Resource
471
+
472
+ end # Stone
@@ -0,0 +1,9 @@
1
+ module Stone #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 2
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bomberstudios-stone
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.2"
5
+ platform: ruby
6
+ authors:
7
+ - "Ale Mu\xC3\xB1oz"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-11 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Super-simple data persistence layer created for small applications.
17
+ email: bomberstudios@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/stone.rb
26
+ - lib/stone/callbacks.rb
27
+ - lib/stone/data_store.rb
28
+ - lib/stone/query.rb
29
+ - lib/stone/resource.rb
30
+ - lib/stone/version.rb
31
+ - lib/stone/core_ext/datetime.rb
32
+ - lib/stone/core_ext/enumerable.rb
33
+ - lib/stone/core_ext/string.rb
34
+ - lib/stone/core_ext/symbol.rb
35
+ has_rdoc: false
36
+ homepage: http://github.com/bomberstudios/stone/
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.8.5
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: Super-simple data persistence layer created for small applications.
61
+ test_files: []
62
+