rhodes 0.1.0

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.
Files changed (53) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +4 -0
  3. data/Manifest.txt +52 -0
  4. data/README.rdoc +2 -0
  5. data/Rakefile +33 -0
  6. data/bin/rhogen +8 -0
  7. data/generators/rhogen.rb +99 -0
  8. data/generators/templates/application/application.rb +4 -0
  9. data/generators/templates/application/index.html +25 -0
  10. data/generators/templates/model/config.rb +3 -0
  11. data/generators/templates/model/controller.rb +48 -0
  12. data/generators/templates/model/edit.erb +21 -0
  13. data/generators/templates/model/index.erb +10 -0
  14. data/generators/templates/model/new.erb +16 -0
  15. data/lib/ServeME.rb +7 -0
  16. data/lib/TestServe.rb +9 -0
  17. data/lib/builtinME.rb +481 -0
  18. data/lib/date.rb +1781 -0
  19. data/lib/date/format.rb +1313 -0
  20. data/lib/erb.rb +896 -0
  21. data/lib/find.rb +81 -0
  22. data/lib/rational.rb +19 -0
  23. data/lib/rho.rb +1 -0
  24. data/lib/rho/render.rb +9 -0
  25. data/lib/rho/renderME.rb +8 -0
  26. data/lib/rho/rho.rb +173 -0
  27. data/lib/rho/rhoapplication.rb +36 -0
  28. data/lib/rho/rhocontroller.rb +51 -0
  29. data/lib/rho/rhofsconnector.rb +39 -0
  30. data/lib/rho/rhofsconnectorME.rb +36 -0
  31. data/lib/rho/rhosupport.rb +139 -0
  32. data/lib/rhodes.rb +5 -0
  33. data/lib/rhofsconnector.rb +5 -0
  34. data/lib/rhom.rb +1 -0
  35. data/lib/rhom/rhom.rb +41 -0
  36. data/lib/rhom/rhom_db_adapter.rb +183 -0
  37. data/lib/rhom/rhom_db_adapterME.rb +91 -0
  38. data/lib/rhom/rhom_object.rb +53 -0
  39. data/lib/rhom/rhom_object_factory.rb +246 -0
  40. data/lib/singleton.rb +313 -0
  41. data/lib/time.rb +839 -0
  42. data/rhodes.gemspec +18 -0
  43. data/spec/app_generator_spec.rb +27 -0
  44. data/spec/generator_spec_helper.rb +12 -0
  45. data/spec/model_generator_spec.rb +28 -0
  46. data/spec/rho_spec.rb +28 -0
  47. data/spec/rhom_object_factory_spec.rb +147 -0
  48. data/spec/spec.opts +1 -0
  49. data/spec/spec_helper.rb +14 -0
  50. data/spec/stubs.rb +17 -0
  51. data/spec/syncdbtest.sqlite +0 -0
  52. data/tasks/rspec.rake +34 -0
  53. metadata +157 -0
@@ -0,0 +1,53 @@
1
+ #
2
+ # rhom_object.rb
3
+ # rhodes
4
+ #
5
+ # Copyright (C) 2008 Lars Burgess. All rights reserved.
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ #
20
+ module Rhom
21
+ module RhomObject
22
+ # defines a method at runtime for the
23
+ # dynamically created class
24
+
25
+
26
+ # we override method_missing here so that instance variables,
27
+ # when retrieved or set, are added to the object
28
+ def method_missing(name, *args)
29
+ unless name == Fixnum
30
+ varname = name.to_s.gsub(/=/,"")
31
+ if instance_variable_defined? "@#{varname}"
32
+ #TODO: Figure out why this returns an array
33
+ instance_variable_get( "@#{varname}" )[0]
34
+ else
35
+ instance_variable_set( "@#{varname}", args )
36
+ end
37
+ end
38
+ end
39
+
40
+ def strip_braces(str=nil)
41
+ str ? str.gsub(/\{/,"").gsub(/\}/,"") : nil
42
+ end
43
+
44
+ # use djb hash function to generate temp object id
45
+ def djb_hash(str, len)
46
+ hash = 5381
47
+ for i in (0..len)
48
+ hash = ((hash << 5) + hash) + str[i].to_i
49
+ end
50
+ return hash
51
+ end
52
+ end # RhomObject
53
+ end # Rhom
@@ -0,0 +1,246 @@
1
+ #
2
+ # rhom_object_factory.rb
3
+ # rhodes
4
+ # Returns an array of RhomObjects
5
+ #
6
+ # Copyright (C) 2008 Lars Burgess. All rights reserved.
7
+ #
8
+ # This program is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ require 'rhom'
22
+ require 'rho'
23
+
24
+ module Rhom
25
+ class RhomObjectFactory
26
+
27
+ def initialize
28
+ unless not defined? Rho::RhoConfig::sources
29
+ init_source_attribs
30
+ init_objects
31
+ end
32
+ end
33
+
34
+ def init_source_attribs
35
+ # merge source attributes into config hash
36
+ # TODO: This shouldn't reference 'source[1]' directly
37
+ Rho::RhoConfig::sources.each do |source|
38
+ src_attribs = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
39
+ 'attrib',
40
+ {"source_id"=>source[1]['source_id'].to_s},
41
+ {"distinct"=>true})
42
+ # update our source with the proper attributes
43
+ source[1].merge!({"attribs"=>src_attribs})
44
+ end
45
+ end
46
+
47
+ # Initialize new object with dynamic attributes
48
+ def init_objects
49
+ Rho::RhoConfig::sources.each do |classname,source|
50
+ unless Object::const_defined?(classname.intern)
51
+ Object::const_set(classname.intern,
52
+ Class::new do
53
+ include ::Rhom::RhomObject
54
+ extend ::Rhom::RhomObject
55
+
56
+ def initialize(obj=nil)
57
+ if obj
58
+ # create a temp id for the create type
59
+ # TODO: This is duplicative of get_new_obj
60
+ temp_objid = djb_hash(obj.values.to_s, 10).to_s
61
+ self.send 'object'.to_sym, "#{temp_objid}"
62
+ self.send 'source_id'.to_sym, obj['source_id'].to_s
63
+ self.send 'update_type'.to_sym, 'create'
64
+ obj.each do |key,value|
65
+ val = self.inst_strip_braces(value)
66
+ self.send key, val
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ class << self
73
+
74
+ def get_source_id
75
+ Rho::RhoConfig::sources[self.name.to_s]['source_id'].to_s
76
+ end
77
+ # retrieve a single record if object id provided, otherwise return
78
+ # full list corresponding to factory's source id
79
+ def find(*args)
80
+ list = []
81
+ if args.first == :all
82
+ result = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
83
+ '*',
84
+ {"source_id"=>get_source_id,"update_type"=>'query'},
85
+ {"order by"=>'object'})
86
+ else
87
+ obj = strip_braces(args.first.to_s)
88
+ result = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
89
+ '*',
90
+ {"object"=>obj})
91
+ end
92
+ list = get_list(result)
93
+ if list.length == 1
94
+ return list[0]
95
+ end
96
+ list
97
+ end
98
+
99
+ def find_by(*args)
100
+ # TODO: implement
101
+ end
102
+
103
+ # returns an array of objects based on an existing array
104
+ def get_list(objs)
105
+ new_list = []
106
+ if objs and defined? Rho::RhoConfig::sources[self.name.to_s]
107
+ attrib_length = Rho::RhoConfig::sources[self.name.to_s]['attribs'].length
108
+ list_length = 0
109
+ list_length = (objs.length / attrib_length) unless attrib_length == 0
110
+ new_obj = nil
111
+ # iterate over the array and determine object
112
+ # structure based on attribute/value pairs
113
+ list_length.times do |i|
114
+ new_obj = get_new_obj(objs[i*attrib_length])
115
+ attrib_length.times do |j|
116
+ # setup index and assign accessors
117
+ idx = i*attrib_length+j
118
+ begin
119
+ # only update attributes if they belong
120
+ # to the current object
121
+ if objs[idx]['object'] == strip_braces((new_obj.send 'object'.to_sym))
122
+ attrib = objs[idx]['attrib'].to_s
123
+ value = objs[idx]['value'].to_s
124
+ new_obj.send attrib.to_sym, value
125
+ end
126
+ rescue
127
+ puts "failed to reference objs[#{idx}]..."
128
+ end
129
+ end
130
+ new_list << new_obj
131
+ end
132
+ else
133
+ # source attributes are not initialized,
134
+ # try again
135
+ RhomObjectFactory::init_sources
136
+ end
137
+ new_list
138
+ end
139
+
140
+ # returns new model instance with a temp object id
141
+ def get_new_obj(obj, type='query')
142
+ tmp_obj = self.new
143
+ tmp_obj.send 'object'.to_sym, "{#{obj['object'].to_s}}"
144
+ tmp_obj.send 'source_id'.to_sym, get_source_id
145
+ tmp_obj.send 'update_type'.to_sym, type
146
+ tmp_obj
147
+ end
148
+ end #class methods
149
+
150
+ # deletes the record from the viewable list as well as
151
+ # adding a delete record to the list of sync operations
152
+ def destroy
153
+ result = nil
154
+ obj = self.inst_strip_braces(self.object)
155
+ if obj
156
+ # first delete the record from viewable list
157
+ result = ::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME,
158
+ {"object"=>obj})
159
+ # now add delete operation
160
+ result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
161
+ {"source_id"=>self.get_inst_source_id,
162
+ "object"=>obj,
163
+ "update_type"=>'delete'})
164
+ end
165
+ result
166
+ end
167
+
168
+ # saves the current object to the database as a create type
169
+ def save
170
+ result = nil
171
+ # iterate over each instance variable and insert create row to table
172
+ obj = self.inst_strip_braces(self.object)
173
+ self.instance_variables.each do |method|
174
+ method = method.to_s.gsub(/@/,"")
175
+ # Don't save objects with braces to database
176
+ val = self.inst_strip_braces(self.send(method.to_sym))
177
+ # add rows excluding object, source_id and update_type
178
+ unless self.method_name_reserved?(method) or val.nil?
179
+ result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
180
+ {"source_id"=>self.get_inst_source_id,
181
+ "object"=>obj,
182
+ "attrib"=>method,
183
+ "value"=>val,
184
+ "update_type"=>'create'})
185
+ end
186
+ end
187
+ # Create a temporary query record to display in the list
188
+ Rho::RhoConfig::sources[self.class.name.to_s]['attribs'].each do |attrib|
189
+ result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
190
+ {"source_id"=>self.get_inst_source_id,
191
+ "object"=>obj,
192
+ "attrib"=>attrib['attrib'],
193
+ "value"=>self.send(attrib['attrib'].to_sym),
194
+ "update_type"=>'query'})
195
+ end
196
+ result
197
+ end
198
+
199
+ # updates the current record in the viewable list and adds
200
+ # a sync operation to update
201
+ def update_attributes(attrs)
202
+ result = nil
203
+ obj = self.inst_strip_braces(self.object)
204
+ self.instance_variables.each do |method|
205
+ method = method.to_s.gsub(/@/,"")
206
+ val = self.send method.to_sym
207
+ # Don't save objects with braces to database
208
+ new_val = self.inst_strip_braces(attrs[method])
209
+ # if the object's value doesn't match the database record
210
+ # then we procede with update
211
+ if new_val and val != new_val
212
+ unless self.method_name_reserved?(method) or new_val.length == 0
213
+ # update viewable list
214
+ result = ::Rhom::RhomDbAdapter::update_into_table(::Rhom::TABLE_NAME,
215
+ {"value"=>new_val},
216
+ {"object"=>obj, "attrib"=>method})
217
+ # update sync list
218
+ result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
219
+ {"source_id"=>self.get_inst_source_id,
220
+ "object"=>obj,
221
+ "attrib"=>method,
222
+ "value"=>new_val,
223
+ "update_type"=>'update'})
224
+ end
225
+ end
226
+ end
227
+ result
228
+ end
229
+
230
+ def get_inst_source_id
231
+ Rho::RhoConfig::sources[self.class.name.to_s]['source_id'].to_s
232
+ end
233
+
234
+ def inst_strip_braces(str=nil)
235
+ str ? str.gsub(/\{/,"").gsub(/\}/,"") : nil
236
+ end
237
+
238
+ def method_name_reserved?(method)
239
+ method =~ /object|source_id|update_type/
240
+ end
241
+ end)
242
+ end
243
+ end
244
+ end
245
+ end # RhomObjectFactory
246
+ end # Rhom
data/lib/singleton.rb ADDED
@@ -0,0 +1,313 @@
1
+ # The Singleton module implements the Singleton pattern.
2
+ #
3
+ # Usage:
4
+ # class Klass
5
+ # include Singleton
6
+ # # ...
7
+ # end
8
+ #
9
+ # * this ensures that only one instance of Klass lets call it
10
+ # ``the instance'' can be created.
11
+ #
12
+ # a,b = Klass.instance, Klass.instance
13
+ # a == b # => true
14
+ # a.new # NoMethodError - new is private ...
15
+ #
16
+ # * ``The instance'' is created at instantiation time, in other
17
+ # words the first call of Klass.instance(), thus
18
+ #
19
+ # class OtherKlass
20
+ # include Singleton
21
+ # # ...
22
+ # end
23
+ # ObjectSpace.each_object(OtherKlass){} # => 0.
24
+ #
25
+ # * This behavior is preserved under inheritance and cloning.
26
+ #
27
+ #
28
+ #
29
+ # This is achieved by marking
30
+ # * Klass.new and Klass.allocate - as private
31
+ #
32
+ # Providing (or modifying) the class methods
33
+ # * Klass.inherited(sub_klass) and Klass.clone() -
34
+ # to ensure that the Singleton pattern is properly
35
+ # inherited and cloned.
36
+ #
37
+ # * Klass.instance() - returning ``the instance''. After a
38
+ # successful self modifying (normally the first) call the
39
+ # method body is a simple:
40
+ #
41
+ # def Klass.instance()
42
+ # return @singleton__instance__
43
+ # end
44
+ #
45
+ # * Klass._load(str) - calling Klass.instance()
46
+ #
47
+ # * Klass._instantiate?() - returning ``the instance'' or
48
+ # nil. This hook method puts a second (or nth) thread calling
49
+ # Klass.instance() on a waiting loop. The return value
50
+ # signifies the successful completion or premature termination
51
+ # of the first, or more generally, current "instantiation thread".
52
+ #
53
+ #
54
+ # The instance method of Singleton are
55
+ # * clone and dup - raising TypeErrors to prevent cloning or duping
56
+ #
57
+ # * _dump(depth) - returning the empty string. Marshalling strips
58
+ # by default all state information, e.g. instance variables and
59
+ # taint state, from ``the instance''. Providing custom _load(str)
60
+ # and _dump(depth) hooks allows the (partially) resurrections of
61
+ # a previous state of ``the instance''.
62
+
63
+ require 'thread'
64
+
65
+ module Singleton
66
+ # disable build-in copying methods
67
+ def clone
68
+ raise TypeError, "can't clone instance of singleton #{self.class}"
69
+ end
70
+ def dup
71
+ raise TypeError, "can't dup instance of singleton #{self.class}"
72
+ end
73
+
74
+ # default marshalling strategy
75
+ def _dump(depth = -1)
76
+ ''
77
+ end
78
+
79
+ module SingletonClassMethods
80
+ # properly clone the Singleton pattern - did you know
81
+ # that duping doesn't copy class methods?
82
+ def clone
83
+ Singleton.__init__(super)
84
+ end
85
+
86
+ def _load(str)
87
+ instance
88
+ end
89
+
90
+ private
91
+
92
+ # ensure that the Singleton pattern is properly inherited
93
+ def inherited(sub_klass)
94
+ super
95
+ Singleton.__init__(sub_klass)
96
+ end
97
+ end
98
+
99
+ class << Singleton
100
+ def __init__(klass)
101
+ klass.instance_eval {
102
+ @singleton__instance__ = nil
103
+ @singleton__mutex__ = Mutex.new
104
+ }
105
+ def klass.instance
106
+ return @singleton__instance__ if @singleton__instance__
107
+ @singleton__mutex__.synchronize {
108
+ return @singleton__instance__ if @singleton__instance__
109
+ @singleton__instance__ = new()
110
+ }
111
+ @singleton__instance__
112
+ end
113
+ klass
114
+ end
115
+
116
+ private
117
+
118
+ # extending an object with Singleton is a bad idea
119
+ undef_method :extend_object
120
+
121
+ def append_features(mod)
122
+ # help out people counting on transitive mixins
123
+ unless mod.instance_of?(Class)
124
+ raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}"
125
+ end
126
+ super
127
+ end
128
+
129
+ def included(klass)
130
+ super
131
+ klass.private_class_method :new, :allocate
132
+ klass.extend SingletonClassMethods
133
+ Singleton.__init__(klass)
134
+ end
135
+ end
136
+
137
+ end
138
+
139
+
140
+ if __FILE__ == $0
141
+
142
+ def num_of_instances(klass)
143
+ "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
144
+ end
145
+
146
+ # The basic and most important example.
147
+
148
+ class SomeSingletonClass
149
+ include Singleton
150
+ end
151
+ puts "There are #{num_of_instances(SomeSingletonClass)}"
152
+
153
+ a = SomeSingletonClass.instance
154
+ b = SomeSingletonClass.instance # a and b are same object
155
+ puts "basic test is #{a == b}"
156
+
157
+ begin
158
+ SomeSingletonClass.new
159
+ rescue NoMethodError => mes
160
+ puts mes
161
+ end
162
+
163
+
164
+
165
+ puts "\nThreaded example with exception and customized #_instantiate?() hook"; p
166
+ Thread.abort_on_exception = false
167
+
168
+ class Ups < SomeSingletonClass
169
+ def initialize
170
+ self.class.__sleep
171
+ puts "initialize called by thread ##{Thread.current[:i]}"
172
+ end
173
+ end
174
+
175
+ class << Ups
176
+ def _instantiate?
177
+ @enter.push Thread.current[:i]
178
+ while false.equal?(@singleton__instance__)
179
+ @singleton__mutex__.unlock
180
+ sleep 0.08
181
+ @singleton__mutex__.lock
182
+ end
183
+ @leave.push Thread.current[:i]
184
+ @singleton__instance__
185
+ end
186
+
187
+ def __sleep
188
+ sleep(rand(0.08))
189
+ end
190
+
191
+ def new
192
+ begin
193
+ __sleep
194
+ raise "boom - thread ##{Thread.current[:i]} failed to create instance"
195
+ ensure
196
+ # simple flip-flop
197
+ class << self
198
+ remove_method :new
199
+ end
200
+ end
201
+ end
202
+
203
+ def instantiate_all
204
+ @enter = []
205
+ @leave = []
206
+ 1.upto(9) {|i|
207
+ Thread.new {
208
+ begin
209
+ Thread.current[:i] = i
210
+ __sleep
211
+ instance
212
+ rescue RuntimeError => mes
213
+ puts mes
214
+ end
215
+ }
216
+ }
217
+ puts "Before there were #{num_of_instances(self)}"
218
+ sleep 3
219
+ puts "Now there is #{num_of_instances(self)}"
220
+ puts "#{@enter.join '; '} was the order of threads entering the waiting loop"
221
+ puts "#{@leave.join '; '} was the order of threads leaving the waiting loop"
222
+ end
223
+ end
224
+
225
+
226
+ Ups.instantiate_all
227
+ # results in message like
228
+ # Before there were 0 Ups instance(s)
229
+ # boom - thread #6 failed to create instance
230
+ # initialize called by thread #3
231
+ # Now there is 1 Ups instance(s)
232
+ # 3; 2; 1; 8; 4; 7; 5 was the order of threads entering the waiting loop
233
+ # 3; 2; 1; 7; 4; 8; 5 was the order of threads leaving the waiting loop
234
+
235
+
236
+ puts "\nLets see if class level cloning really works"
237
+ Yup = Ups.clone
238
+ def Yup.new
239
+ begin
240
+ __sleep
241
+ raise "boom - thread ##{Thread.current[:i]} failed to create instance"
242
+ ensure
243
+ # simple flip-flop
244
+ class << self
245
+ remove_method :new
246
+ end
247
+ end
248
+ end
249
+ Yup.instantiate_all
250
+
251
+
252
+ puts "\n\n","Customized marshalling"
253
+ class A
254
+ include Singleton
255
+ attr_accessor :persist, :die
256
+ def _dump(depth)
257
+ # this strips the @die information from the instance
258
+ Marshal.dump(@persist,depth)
259
+ end
260
+ end
261
+
262
+ def A._load(str)
263
+ instance.persist = Marshal.load(str)
264
+ instance
265
+ end
266
+
267
+ a = A.instance
268
+ a.persist = ["persist"]
269
+ a.die = "die"
270
+ a.taint
271
+
272
+ stored_state = Marshal.dump(a)
273
+ # change state
274
+ a.persist = nil
275
+ a.die = nil
276
+ b = Marshal.load(stored_state)
277
+ p a == b # => true
278
+ p a.persist # => ["persist"]
279
+ p a.die # => nil
280
+
281
+
282
+ puts "\n\nSingleton with overridden default #inherited() hook"
283
+ class Up
284
+ end
285
+ def Up.inherited(sub_klass)
286
+ puts "#{sub_klass} subclasses #{self}"
287
+ end
288
+
289
+
290
+ class Middle < Up
291
+ include Singleton
292
+ end
293
+
294
+ class Down < Middle; end
295
+
296
+ puts "and basic \"Down test\" is #{Down.instance == Down.instance}\n
297
+ Various exceptions"
298
+
299
+ begin
300
+ module AModule
301
+ include Singleton
302
+ end
303
+ rescue TypeError => mes
304
+ puts mes #=> Inclusion of the OO-Singleton module in module AModule
305
+ end
306
+
307
+ begin
308
+ 'aString'.extend Singleton
309
+ rescue NoMethodError => mes
310
+ puts mes #=> undefined method `extend_object' for Singleton:Module
311
+ end
312
+
313
+ end