rhodes-framework 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +37 -0
  3. data/Manifest.txt +66 -0
  4. data/README.rdoc +2 -0
  5. data/Rakefile +50 -0
  6. data/lib/ServeME.rb +7 -0
  7. data/lib/TestServe.rb +9 -0
  8. data/lib/bsearch.rb +120 -0
  9. data/lib/builtinME.rb +626 -0
  10. data/lib/date/format.rb +1339 -0
  11. data/lib/date.rb +1792 -0
  12. data/lib/dateME.rb +24 -0
  13. data/lib/erb.rb +896 -0
  14. data/lib/find.rb +81 -0
  15. data/lib/rational.rb +19 -0
  16. data/lib/rationalME.rb +530 -0
  17. data/lib/rho/render.rb +51 -0
  18. data/lib/rho/rho.rb +255 -0
  19. data/lib/rho/rhoapplication.rb +36 -0
  20. data/lib/rho/rhocontact.rb +110 -0
  21. data/lib/rho/rhocontroller.rb +35 -0
  22. data/lib/rho/rhofsconnector.rb +32 -0
  23. data/lib/rho/rhosupport.rb +146 -0
  24. data/lib/rho/rhoviewhelpers.rb +130 -0
  25. data/lib/rho.rb +1 -0
  26. data/lib/rhodes-framework.rb +2 -0
  27. data/lib/rhodes.rb +9 -0
  28. data/lib/rhoframework.rb +38 -0
  29. data/lib/rhofsconnector.rb +1 -0
  30. data/lib/rhom/rhom.rb +58 -0
  31. data/lib/rhom/rhom_db_adapter.rb +185 -0
  32. data/lib/rhom/rhom_db_adapterME.rb +93 -0
  33. data/lib/rhom/rhom_object.rb +69 -0
  34. data/lib/rhom/rhom_object_factory.rb +309 -0
  35. data/lib/rhom/rhom_source.rb +60 -0
  36. data/lib/rhom.rb +1 -0
  37. data/lib/singleton.rb +137 -0
  38. data/lib/time.rb +489 -0
  39. data/lib/version.rb +8 -0
  40. data/res/sqlite3/constants.rb +49 -0
  41. data/res/sqlite3/database.rb +715 -0
  42. data/res/sqlite3/driver/dl/api.rb +154 -0
  43. data/res/sqlite3/driver/dl/driver.rb +307 -0
  44. data/res/sqlite3/driver/native/driver.rb +257 -0
  45. data/res/sqlite3/errors.rb +68 -0
  46. data/res/sqlite3/pragmas.rb +271 -0
  47. data/res/sqlite3/resultset.rb +176 -0
  48. data/res/sqlite3/sqlite3_api.rb +0 -0
  49. data/res/sqlite3/statement.rb +230 -0
  50. data/res/sqlite3/translator.rb +109 -0
  51. data/res/sqlite3/value.rb +57 -0
  52. data/res/sqlite3/version.rb +14 -0
  53. data/rhodes-framework.gemspec +18 -0
  54. data/rhodes.gemspec +18 -0
  55. data/spec/app_manifest.txt +4 -0
  56. data/spec/configs/account.rb +3 -0
  57. data/spec/configs/case.rb +3 -0
  58. data/spec/configs/employee.rb +3 -0
  59. data/spec/rho_controller_spec.rb +144 -0
  60. data/spec/rho_spec.rb +75 -0
  61. data/spec/rhom_object_factory_spec.rb +372 -0
  62. data/spec/rhom_spec.rb +45 -0
  63. data/spec/spec.opts +1 -0
  64. data/spec/spec_helper.rb +49 -0
  65. data/spec/stubs.rb +39 -0
  66. data/spec/syncdbtest.sqlite +0 -0
  67. metadata +202 -0
@@ -0,0 +1,309 @@
1
+ #
2
+ # rhom_object_factory.rb
3
+ # rhodes
4
+ # Returns an array of RhomObjects
5
+ #
6
+ # Copyright (C) 2008 Rhomobile, Inc. 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
+ require 'rho/rhosupport'
24
+
25
+ module Rhom
26
+ class RhomObjectFactory
27
+
28
+ def initialize
29
+ unless not defined? Rho::RhoConfig::sources
30
+ init_objects
31
+ end
32
+ end
33
+
34
+ # Initialize new object with dynamic attributes
35
+ def init_objects
36
+ Rho::RhoConfig::sources.each do |classname,source|
37
+ unless Object::const_defined?(classname.intern)
38
+ Object::const_set(classname.intern,
39
+ Class::new do
40
+ include ::Rhom::RhomObject
41
+ extend ::Rhom::RhomObject
42
+
43
+ def initialize(obj=nil)
44
+ self.send("object=".to_sym(), "#{Time.now.to_i}")
45
+ if obj
46
+ self.send("source_id=".to_sym(), obj['source_id'].to_s)
47
+ self.send("update_type=".to_sym(), 'create')
48
+ obj.each do |key,value|
49
+ val = self.inst_strip_braces(value)
50
+ self.send("#{key}=".to_sym(), val)
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ class << self
57
+
58
+ def count
59
+ ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
60
+ 'object',
61
+ {"source_id"=>get_source_id},
62
+ {"distinct"=>true}).length
63
+ end
64
+
65
+ def get_source_id
66
+ Rho::RhoConfig::sources[self.name.to_s]['source_id'].to_s
67
+ end
68
+
69
+ # retrieve a single record if object id provided, otherwise return
70
+ # full list corresponding to factory's source id
71
+ def find(*args)
72
+ list = []
73
+ hash_list = {}
74
+ conditions = {}
75
+
76
+ # first find all query objects
77
+ if args.first == :all
78
+ conditions = {"source_id"=>get_source_id}
79
+ elsif args.first.is_a?(String)
80
+ conditions = {"object"=>strip_braces(args.first.to_s)}
81
+ end
82
+
83
+ # do we have conditions?
84
+ # if so, add them to the query
85
+ condition_hash = {}
86
+ condition_hash = get_conditions_hash(args[1][:conditions]) if args[1] and args[1][:conditions] and args[1][:conditions].is_a?(Hash)
87
+ conditions.merge!(condition_hash)
88
+
89
+ # process query, create, and update lists in order
90
+ ["query", "create", "update"].each do |update_type|
91
+ conditions.merge!({"update_type"=>update_type})
92
+ objs = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME, '*', conditions, {"order by"=>'object'})
93
+
94
+ # fetch the rest of the attributes if we're searching by specific attrib value
95
+ if condition_hash and condition_hash.size > 0
96
+ full_objects = []
97
+ objs.each do |obj|
98
+ full_objects += ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME, '*', {'object' => obj['object'].to_s})
99
+ end
100
+ objs = full_objects
101
+ end
102
+
103
+ # build up the object array where each
104
+ # row in this array is a rhom_object
105
+ objs.collect! do |obj|
106
+ object = obj['object']
107
+ attrib = obj['attrib']
108
+ value = obj['value']
109
+ hash_list[object] = get_new_obj(obj) if not hash_list[object]
110
+ if not method_name_reserved?(attrib) and hash_list[object].send(attrib.to_sym)
111
+ hash_list[object].remove_var(attrib)
112
+ end
113
+ hash_list[object].send("#{attrib}=".to_sym(), value) if not method_name_reserved?(attrib)
114
+ nil # remove the element from the array
115
+ end
116
+
117
+ end
118
+
119
+ # convert hash to array
120
+ list = hash_list.values
121
+ hash_list = nil
122
+
123
+ # setup order by if provided
124
+ order = extract_options(args)
125
+ order_value = order[:order] if order and order[:order]
126
+ if order_value
127
+ order_sym = order_value.to_sym
128
+ list.sort! {|x,y| x.send(order_sym) && y.send(order_sym) ? x.send(order_sym) <=> y.send(order_sym) : 0}
129
+ end
130
+
131
+ # return a single rhom object if searching for one
132
+ if args.first == :first or args.first.is_a?(String)
133
+ return list[0]
134
+ end
135
+ list
136
+ end
137
+
138
+ def find_all(args={})
139
+ find(args)
140
+ end
141
+
142
+ def set_notification(url,params)
143
+ SyncEngine.set_notification(get_source_id.to_i,url,params)
144
+ end
145
+
146
+ def clear_notification
147
+ SyncEngine.clear_notification(get_source_id.to_i)
148
+ end
149
+
150
+ def ask(question)
151
+ tmp_obj = get_new_obj(:object =>djb_hash("#{question}#{rand.to_s}", 10).to_s)
152
+ if question
153
+ # We only support one ask at a time!
154
+ ::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME,
155
+ {"source_id"=>get_source_id,
156
+ "update_type"=>'ask'})
157
+ ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
158
+ {"source_id"=>get_source_id,
159
+ "object"=>tmp_obj.object,
160
+ "attrib"=>'question',
161
+ "value"=>Rho::RhoSupport.url_encode(question),
162
+ "update_type"=>'ask'})
163
+ SyncEngine::dosync
164
+ end
165
+ end
166
+
167
+ # deletes all records matching conditions (optionally nil)
168
+ def delete_all(conditions=nil)
169
+ if conditions
170
+ del_conditions = get_conditions_hash(conditions[:conditions])
171
+ # find all relevant objects, then delete them
172
+ del_objects = ::Rhom::RhomDbAdapter::select_from_table(::Rhom::TABLE_NAME,
173
+ 'object',
174
+ del_conditions.merge!({"source_id"=>get_source_id}),
175
+ {"distinct"=>true})
176
+ del_objects.each do |obj|
177
+ ::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME, {'object'=>obj['object']})
178
+ end
179
+ else
180
+ ::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME, {"source_id"=>get_source_id})
181
+ end
182
+ end
183
+
184
+ private
185
+ # returns new model instance with a temp object id
186
+ def get_new_obj(obj)
187
+ tmp_obj = self.new
188
+ tmp_obj.send("object=".to_sym(), "{#{obj['object'].to_s}}")
189
+ tmp_obj
190
+ end
191
+
192
+ # get hash of conditions in sql form
193
+ def get_conditions_hash(conditions=nil)
194
+ if conditions
195
+ condition_hash = {}
196
+ conditions.each do |key,value|
197
+ condition_hash.merge!('attrib' => key.to_s, 'value' => value.to_s)
198
+ end
199
+ condition_hash
200
+ else
201
+ nil
202
+ end
203
+ end
204
+ end #class methods
205
+
206
+ # deletes the record from the viewable list as well as
207
+ # adding a delete record to the list of sync operations
208
+ def destroy
209
+ result = nil
210
+ obj = self.inst_strip_braces(self.object)
211
+ update_type=self.get_update_type_by_source('delete')
212
+ if obj
213
+ # first delete the record from viewable list
214
+ result = ::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME,
215
+ {"object"=>obj})
216
+ if update_type
217
+ # now add delete operation
218
+ result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
219
+ {"source_id"=>self.get_inst_source_id,
220
+ "object"=>obj,
221
+ "update_type"=>update_type})
222
+ end
223
+ end
224
+ result
225
+ end
226
+
227
+ # saves the current object to the database as a create type
228
+ def save
229
+ result = nil
230
+ # iterate over each instance variable and insert create row to table
231
+ obj = self.inst_strip_braces(self.object)
232
+ update_type=self.get_update_type_by_source('create')
233
+ self.instance_variables.each do |method|
234
+ method = method.to_s.gsub(/@/,"")
235
+ # Don't save objects with braces to database
236
+ val = self.inst_strip_braces(self.send(method.to_sym))
237
+ # add rows excluding object, source_id and update_type
238
+ unless self.method_name_reserved?(method) or val.nil?
239
+ fields = {"source_id"=>self.get_inst_source_id,
240
+ "object"=>obj,
241
+ "attrib"=>method,
242
+ "value"=>val,
243
+ "update_type"=>update_type}
244
+ fields = method == "image_uri" ? fields.merge!({"attrib_type" => "blob.file"}) : fields
245
+ result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME, fields)
246
+ end
247
+ end
248
+ result
249
+ end
250
+
251
+ # updates the current record in the viewable list and adds
252
+ # a sync operation to update
253
+ def update_attributes(attrs)
254
+ result = nil
255
+ obj = self.inst_strip_braces(self.object)
256
+ update_type=self.get_update_type_by_source('update')
257
+ attrs.each do |attrib,val|
258
+ attrib = attrib.to_s.gsub(/@/,"")
259
+ old_val = self.send attrib.to_sym unless self.method_name_reserved?(attrib)
260
+
261
+ # Don't save objects with braces to database
262
+ new_val = self.inst_strip_braces(val)
263
+
264
+ # if the object's value doesn't match the database record
265
+ # then we procede with update
266
+ if new_val and old_val != new_val
267
+ unless self.method_name_reserved?(attrib) or new_val.length == 0
268
+ ::Rhom::RhomDbAdapter::delete_from_table(::Rhom::TABLE_NAME,
269
+ {"source_id"=>self.get_inst_source_id,
270
+ "object"=>obj,
271
+ "attrib"=>attrib,
272
+ "update_type"=>update_type})
273
+ # update sync list
274
+ result = ::Rhom::RhomDbAdapter::insert_into_table(::Rhom::TABLE_NAME,
275
+ {"source_id"=>self.get_inst_source_id,
276
+ "object"=>obj,
277
+ "attrib"=>attrib,
278
+ "value"=>new_val,
279
+ "update_type"=>update_type})
280
+ end
281
+ end
282
+ end
283
+ result
284
+ end
285
+
286
+ def get_inst_source_id
287
+ Rho::RhoConfig::sources[self.class.name.to_s]['source_id'].to_s
288
+ end
289
+
290
+ def get_update_type_by_source(update_type)
291
+ source_type = Rho::RhoConfig::sources[self.class.name.to_s]['type']
292
+ if source_type and source_type == "ask" and update_type == 'delete'
293
+ nil
294
+ elsif source_type and source_type == "ask"
295
+ "query"
296
+ else
297
+ update_type
298
+ end
299
+ end
300
+
301
+ def inst_strip_braces(str=nil)
302
+ str ? str.gsub(/\{/,"").gsub(/\}/,"") : nil
303
+ end
304
+ end)
305
+ end
306
+ end
307
+ end
308
+ end # RhomObjectFactory
309
+ end # Rhom
@@ -0,0 +1,60 @@
1
+ require 'time'
2
+ require 'rhom/rhom_object'
3
+
4
+ module Rhom
5
+ class RhomSource
6
+ include RhomObject
7
+ attr_accessor :source_url
8
+ attr_reader :source_id, :name, :last_updated, :last_inserted_size,
9
+ :last_deleted_size, :last_sync_duration,
10
+ :last_sync_success, :distinct_objects
11
+
12
+ def initialize(args,count=0)
13
+ # setup the name
14
+ # TODO: should really store this in the database
15
+ Rho::RhoConfig::sources.each do |key,value|
16
+ if value['source_id'].to_i == args['source_id'].to_i
17
+ @name = key
18
+ end
19
+ end
20
+ @source_id = args['source_id'].to_i
21
+ @source_url = args['source_url']
22
+ @last_updated = Time.at(args['last_updated'].to_i).to_s
23
+ @last_inserted_size = args['last_inserted_size'].to_i
24
+ @last_deleted_size = args['last_deleted_size'].to_i
25
+ @last_sync_duration = args['last_sync_duration'].to_i
26
+ @last_sync_success = args['last_sync_success'].to_i == 1 ? true : false
27
+ @distinct_objects = ::Rhom::RhomDbAdapter::select_from_table(
28
+ ::Rhom::TABLE_NAME,
29
+ 'object',
30
+ {"source_id"=>@source_id},
31
+ {"distinct"=>true}).length
32
+ end
33
+
34
+ class << self
35
+ include RhomObject
36
+ def find(*args)
37
+ list = []
38
+ if args.first == :all
39
+ results = ::Rhom::RhomDbAdapter::select_from_table('sources', '*')
40
+
41
+ results.each do |result|
42
+ list << RhomSource.new(result)
43
+ end
44
+ else
45
+ result = ::Rhom::RhomDbAdapter::select_from_table('sources', '*',
46
+ {"source_id" => strip_braces(args.first)}).first
47
+ list << RhomSource.new(result)
48
+ end
49
+ list.size > 1 ? list : list[0]
50
+ end
51
+ end
52
+
53
+ def update_attributes(params=nil)
54
+ if params
55
+ ::Rhom::RhomDbAdapter::update_into_table('sources', {"source_url"=>params['source_url']},
56
+ {"source_id"=>strip_braces(params['source_id'])})
57
+ end
58
+ end
59
+ end
60
+ end
data/lib/rhom.rb ADDED
@@ -0,0 +1 @@
1
+ require 'rhom/rhom'
data/lib/singleton.rb ADDED
@@ -0,0 +1,137 @@
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