bomberstudios-stone 0.2

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.
@@ -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
+