rhosync 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (200) hide show
  1. data/CHANGELOG +5 -0
  2. data/LICENSE +674 -0
  3. data/README.md +26 -0
  4. data/Rakefile +109 -0
  5. data/bench/bench +6 -0
  6. data/bench/benchapp/Rakefile +14 -0
  7. data/bench/benchapp/application.rb +13 -0
  8. data/bench/benchapp/config.ru +32 -0
  9. data/bench/benchapp/settings/license.key +1 -0
  10. data/bench/benchapp/settings/settings.yml +18 -0
  11. data/bench/benchapp/sources/mock_adapter.rb +55 -0
  12. data/bench/benchapp/sources/queue_mock_adapter.rb +2 -0
  13. data/bench/benchapp/vendor/rhosync/lib/rhosync.rb +7 -0
  14. data/bench/lib/bench/cli.rb +16 -0
  15. data/bench/lib/bench/logging.rb +18 -0
  16. data/bench/lib/bench/mock_client.rb +41 -0
  17. data/bench/lib/bench/result.rb +50 -0
  18. data/bench/lib/bench/runner.rb +44 -0
  19. data/bench/lib/bench/session.rb +65 -0
  20. data/bench/lib/bench/statistics.rb +56 -0
  21. data/bench/lib/bench/test_data.rb +55 -0
  22. data/bench/lib/bench/timer.rb +10 -0
  23. data/bench/lib/bench/utils.rb +49 -0
  24. data/bench/lib/bench.rb +128 -0
  25. data/bench/lib/testdata/100-data.txt +148 -0
  26. data/bench/lib/testdata/5-data.txt +11 -0
  27. data/bench/scripts/cud_script.rb +77 -0
  28. data/bench/scripts/helpers.rb +101 -0
  29. data/bench/scripts/query_md_script.rb +46 -0
  30. data/bench/scripts/query_script.rb +46 -0
  31. data/bench/spec/bench_spec_helper.rb +65 -0
  32. data/bench/spec/logging_spec.rb +19 -0
  33. data/bench/spec/mock_adapter_spec.rb +61 -0
  34. data/bench/spec/mock_client_spec.rb +64 -0
  35. data/bench/spec/result_spec.rb +59 -0
  36. data/bench/spec/utils_spec.rb +35 -0
  37. data/bin/rhosync +34 -0
  38. data/doc/protocol.html +1901 -0
  39. data/doc/public/css/print.css +29 -0
  40. data/doc/public/css/screen.css +257 -0
  41. data/doc/public/css/style.css +20 -0
  42. data/examples/simple/application.rb +13 -0
  43. data/examples/simple/sources/sample_adapter.rb +5 -0
  44. data/examples/simple/sources/simple_adapter.rb +5 -0
  45. data/examples/simple/vendor/rhosync/lib/rhosync.rb +7 -0
  46. data/generators/rhosync.rb +98 -0
  47. data/generators/templates/application/Rakefile +19 -0
  48. data/generators/templates/application/application.rb +27 -0
  49. data/generators/templates/application/config.ru +33 -0
  50. data/generators/templates/application/settings/license.key +1 -0
  51. data/generators/templates/application/settings/settings.yml +14 -0
  52. data/generators/templates/source/source_adapter.rb +49 -0
  53. data/lib/rhosync/api/create_client.rb +3 -0
  54. data/lib/rhosync/api/create_user.rb +7 -0
  55. data/lib/rhosync/api/delete_client.rb +5 -0
  56. data/lib/rhosync/api/delete_user.rb +5 -0
  57. data/lib/rhosync/api/get_api_token.rb +7 -0
  58. data/lib/rhosync/api/get_client_params.rb +3 -0
  59. data/lib/rhosync/api/get_db_doc.rb +7 -0
  60. data/lib/rhosync/api/get_license_info.rb +7 -0
  61. data/lib/rhosync/api/get_source_params.rb +3 -0
  62. data/lib/rhosync/api/list_client_docs.rb +12 -0
  63. data/lib/rhosync/api/list_clients.rb +3 -0
  64. data/lib/rhosync/api/list_source_docs.rb +10 -0
  65. data/lib/rhosync/api/list_sources.rb +15 -0
  66. data/lib/rhosync/api/list_users.rb +3 -0
  67. data/lib/rhosync/api/ping.rb +7 -0
  68. data/lib/rhosync/api/push_deletes.rb +6 -0
  69. data/lib/rhosync/api/push_objects.rb +6 -0
  70. data/lib/rhosync/api/reset.rb +10 -0
  71. data/lib/rhosync/api/set_db_doc.rb +8 -0
  72. data/lib/rhosync/api/set_refresh_time.rb +8 -0
  73. data/lib/rhosync/api/update_user.rb +4 -0
  74. data/lib/rhosync/api/upload_file.rb +4 -0
  75. data/lib/rhosync/api_token.rb +19 -0
  76. data/lib/rhosync/app.rb +69 -0
  77. data/lib/rhosync/bulk_data/bulk_data.rb +75 -0
  78. data/lib/rhosync/bulk_data/syncdb.index.schema +3 -0
  79. data/lib/rhosync/bulk_data/syncdb.schema +37 -0
  80. data/lib/rhosync/bulk_data.rb +2 -0
  81. data/lib/rhosync/client.rb +74 -0
  82. data/lib/rhosync/client_sync.rb +296 -0
  83. data/lib/rhosync/console/app/helpers/auth_helper.rb +18 -0
  84. data/lib/rhosync/console/app/helpers/extensions.rb +19 -0
  85. data/lib/rhosync/console/app/helpers/helpers.rb +52 -0
  86. data/lib/rhosync/console/app/public/main.css +7 -0
  87. data/lib/rhosync/console/app/public/text.txt +0 -0
  88. data/lib/rhosync/console/app/routes/auth.rb +29 -0
  89. data/lib/rhosync/console/app/routes/client.rb +32 -0
  90. data/lib/rhosync/console/app/routes/docs.rb +84 -0
  91. data/lib/rhosync/console/app/routes/home.rb +22 -0
  92. data/lib/rhosync/console/app/routes/user.rb +63 -0
  93. data/lib/rhosync/console/app/views/client.erb +30 -0
  94. data/lib/rhosync/console/app/views/doc.erb +56 -0
  95. data/lib/rhosync/console/app/views/docs.erb +29 -0
  96. data/lib/rhosync/console/app/views/index.erb +50 -0
  97. data/lib/rhosync/console/app/views/layout.erb +12 -0
  98. data/lib/rhosync/console/app/views/newuser.erb +17 -0
  99. data/lib/rhosync/console/app/views/ping.erb +28 -0
  100. data/lib/rhosync/console/app/views/result.erb +11 -0
  101. data/lib/rhosync/console/app/views/user.erb +32 -0
  102. data/lib/rhosync/console/app/views/users.erb +14 -0
  103. data/lib/rhosync/console/rhosync_api.rb +102 -0
  104. data/lib/rhosync/console/server.rb +27 -0
  105. data/lib/rhosync/credential.rb +9 -0
  106. data/lib/rhosync/document.rb +43 -0
  107. data/lib/rhosync/indifferent_access.rb +132 -0
  108. data/lib/rhosync/jobs/bulk_data_job.rb +104 -0
  109. data/lib/rhosync/jobs/ping_job.rb +19 -0
  110. data/lib/rhosync/jobs/source_job.rb +16 -0
  111. data/lib/rhosync/license.rb +79 -0
  112. data/lib/rhosync/lock_ops.rb +11 -0
  113. data/lib/rhosync/model.rb +410 -0
  114. data/lib/rhosync/ping/blackberry.rb +55 -0
  115. data/lib/rhosync/ping/iphone.rb +44 -0
  116. data/lib/rhosync/ping.rb +2 -0
  117. data/lib/rhosync/read_state.rb +27 -0
  118. data/lib/rhosync/server/views/index.erb +12 -0
  119. data/lib/rhosync/server.rb +242 -0
  120. data/lib/rhosync/source.rb +112 -0
  121. data/lib/rhosync/source_adapter.rb +95 -0
  122. data/lib/rhosync/source_sync.rb +245 -0
  123. data/lib/rhosync/store.rb +199 -0
  124. data/lib/rhosync/tasks.rb +151 -0
  125. data/lib/rhosync/user.rb +83 -0
  126. data/lib/rhosync/version.rb +3 -0
  127. data/lib/rhosync.rb +251 -0
  128. data/spec/api/api_helper.rb +44 -0
  129. data/spec/api/create_client_spec.rb +13 -0
  130. data/spec/api/create_user_spec.rb +16 -0
  131. data/spec/api/delete_client_spec.rb +13 -0
  132. data/spec/api/delete_user_spec.rb +18 -0
  133. data/spec/api/get_api_token_spec.rb +25 -0
  134. data/spec/api/get_client_params_spec.rb +18 -0
  135. data/spec/api/get_db_doc_spec.rb +21 -0
  136. data/spec/api/get_license_info_spec.rb +16 -0
  137. data/spec/api/get_source_params_spec.rb +26 -0
  138. data/spec/api/list_client_docs_spec.rb +33 -0
  139. data/spec/api/list_clients_spec.rb +23 -0
  140. data/spec/api/list_source_docs_spec.rb +26 -0
  141. data/spec/api/list_sources_spec.rb +27 -0
  142. data/spec/api/list_users_spec.rb +21 -0
  143. data/spec/api/ping_spec.rb +24 -0
  144. data/spec/api/push_deletes_spec.rb +16 -0
  145. data/spec/api/push_objects_spec.rb +27 -0
  146. data/spec/api/reset_spec.rb +22 -0
  147. data/spec/api/set_db_doc_spec.rb +20 -0
  148. data/spec/api/set_refresh_time_spec.rb +43 -0
  149. data/spec/api/update_user_spec.rb +31 -0
  150. data/spec/api/upload_file_spec.rb +26 -0
  151. data/spec/api_token_spec.rb +13 -0
  152. data/spec/app_spec.rb +20 -0
  153. data/spec/apps/rhotestapp/Rakefile +1 -0
  154. data/spec/apps/rhotestapp/application.rb +16 -0
  155. data/spec/apps/rhotestapp/config.ru +1 -0
  156. data/spec/apps/rhotestapp/settings/apple_fake_cert.pem +1 -0
  157. data/spec/apps/rhotestapp/settings/license.key +1 -0
  158. data/spec/apps/rhotestapp/settings/settings.yml +23 -0
  159. data/spec/apps/rhotestapp/sources/base_adapter.rb +9 -0
  160. data/spec/apps/rhotestapp/sources/sample_adapter.rb +66 -0
  161. data/spec/apps/rhotestapp/sources/simple_adapter.rb +39 -0
  162. data/spec/apps/rhotestapp/sources/sub_adapter.rb +7 -0
  163. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb +8 -0
  164. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem.rb +1 -0
  165. data/spec/bulk_data/bulk_data_spec.rb +79 -0
  166. data/spec/client_spec.rb +58 -0
  167. data/spec/client_sync_spec.rb +377 -0
  168. data/spec/doc/base.html +72 -0
  169. data/spec/doc/doc_spec.rb +303 -0
  170. data/spec/doc/footer.html +4 -0
  171. data/spec/doc/header.html +30 -0
  172. data/spec/document_spec.rb +27 -0
  173. data/spec/generator/generator_spec.rb +53 -0
  174. data/spec/generator/generator_spec_helper.rb +8 -0
  175. data/spec/jobs/bulk_data_job_spec.rb +76 -0
  176. data/spec/jobs/ping_job_spec.rb +26 -0
  177. data/spec/jobs/source_job_spec.rb +25 -0
  178. data/spec/license_spec.rb +48 -0
  179. data/spec/model_spec.rb +269 -0
  180. data/spec/perf/bulk_data_perf_spec.rb +33 -0
  181. data/spec/perf/perf_spec_helper.rb +51 -0
  182. data/spec/perf/store_perf_spec.rb +28 -0
  183. data/spec/ping/blackberry_spec.rb +62 -0
  184. data/spec/ping/iphone_spec.rb +50 -0
  185. data/spec/read_state_spec.rb +25 -0
  186. data/spec/rhosync_spec.rb +43 -0
  187. data/spec/server/server_spec.rb +341 -0
  188. data/spec/source_adapter_spec.rb +114 -0
  189. data/spec/source_spec.rb +77 -0
  190. data/spec/source_sync_spec.rb +248 -0
  191. data/spec/spec_helper.rb +240 -0
  192. data/spec/store_spec.rb +149 -0
  193. data/spec/sync_states_spec.rb +101 -0
  194. data/spec/testdata/1000-data.txt +1414 -0
  195. data/spec/testdata/compressed/compress-data.txt +1 -0
  196. data/spec/testdata/upload1.txt +1 -0
  197. data/spec/testdata/upload2.txt +1 -0
  198. data/spec/user_spec.rb +79 -0
  199. data/tasks/redis.rake +134 -0
  200. metadata +545 -0
@@ -0,0 +1,410 @@
1
+ module Rhosync
2
+ # Taken from http://github.com/voloko/redis-model
3
+ #
4
+ # Simple models for redis-rb.
5
+
6
+ class Model
7
+ attr_accessor :id
8
+
9
+ def initialize(id=nil)
10
+ self.id = id
11
+ end
12
+
13
+ def redis #:nodoc:
14
+ self.class.redis
15
+ end
16
+
17
+ # Issues delete commands for all defined fields
18
+ def delete(name = nil)
19
+ if name
20
+ redis.del field_key(name.to_s)
21
+ else
22
+ self.class.fields.each do |field|
23
+ redis.del field_key(field[:name])
24
+ end
25
+ end
26
+ end
27
+
28
+ def field_key(name) #:nodoc:
29
+ self.class._field_key(prefix,id,name)
30
+ end
31
+
32
+ # Increment the specified integer field by 1 or the
33
+ # specified amount.
34
+ def increment!(name,amount=1)
35
+ raise ArgumentError, "Only integer fields can be incremented." unless self.class.fields.include?({:name => name.to_s, :type => :integer})
36
+ redis.incr(field_key(name), amount)
37
+ end
38
+
39
+ # Decrement the specified integer field by 1 or the
40
+ # specified amount.
41
+ def decrement!(name,amount=1)
42
+ raise ArgumentError, "Only integer fields can be decremented." unless self.class.fields.include?({:name => name.to_s, :type => :integer})
43
+ redis.decr(field_key(name), amount)
44
+ end
45
+
46
+ def next_id #:nodoc:
47
+ redis.incr "sequence:#{self.prefix}:id"
48
+ end
49
+
50
+ def self.is_exist?(id)
51
+ !redis.get(self._field_key(self._prefix,id,'rho__id')).nil?
52
+ end
53
+
54
+ def to_array
55
+ res = []
56
+ self.class.fields.each do |field|
57
+ res << field.merge!(:value => send(field[:name].to_sym))
58
+ end
59
+ res
60
+ end
61
+
62
+ def update(attribs)
63
+ self.class.fields.each do |field|
64
+ if field[:name] != 'name' and field[:name] != 'rho__id'
65
+ redis.del field_key(field[:name])
66
+ end
67
+ end
68
+ self.class.populate_attributes(self,attribs)
69
+ end
70
+
71
+ protected
72
+ def prefix #:nodoc:
73
+ @prefix ||= self.class.prefix || self.class.class_prefix(self.class)
74
+ end
75
+
76
+ class << self
77
+ # Defaults to model_name.dasherize
78
+ attr_accessor :prefix
79
+ attr_accessor :validates_presence
80
+
81
+ def _prefix
82
+ class_prefix(self)
83
+ end
84
+
85
+ def _field_key(p,i,n) #:nodoc:
86
+ "#{p}:#{i}:#{n}"
87
+ end
88
+
89
+ def class_prefix(classname)
90
+ classname.to_s.
91
+ sub(%r{(.*::)}, '').
92
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
93
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
94
+ downcase
95
+ end
96
+
97
+ # Creates new model instance with new uniqid
98
+ # NOTE: "sequence:model_name:id" key is used
99
+ def create(params = {}, attributes = {})
100
+ raise ArgumentError.new("Record already exists for '#{params[:id]}'") if self.is_exist?(params[:id])
101
+ if self.validates_presence
102
+ self.validates_presence.each do |field|
103
+ raise ArgumentError.new("Missing required field '#{field}'") unless params[field]
104
+ end
105
+ end
106
+ o = self.new
107
+ o.id = params[:id].nil? ? o.next_id : params[:id]
108
+ params[:rho__id] = params[:id]
109
+ populate_model(o,params)
110
+ populate_attributes(o,attributes)
111
+ end
112
+
113
+ def load(id, params={})
114
+ return unless self.is_exist?(id)
115
+ populate_attributes(self.with_key(id),params)
116
+ end
117
+
118
+ def populate_attributes(obj,attribs)
119
+ attribs.each do |attrib,value|
120
+ obj.send "#{attrib.to_s}=".to_sym, value
121
+ end
122
+ obj
123
+ end
124
+
125
+ def validates_presence_of(*names)
126
+ self.validates_presence ||= []
127
+ names.each do |name|
128
+ self.validates_presence << name
129
+ end
130
+ end
131
+
132
+ # Creates new model instance with given id
133
+ alias_method :with_key, :new
134
+ alias_method :with_next_key, :create
135
+
136
+ # Defines marshaled rw accessor for redis string value
137
+ def field(name, type = :string)
138
+ if @fields.nil?
139
+ @fields = []
140
+ field :rho__id, :string
141
+ end
142
+ type = type.to_sym
143
+ type = :integer if type == :int
144
+
145
+ class_name = marshal_class_name(name, type)
146
+
147
+ fields << {:name => name.to_s, :type => type}
148
+ if type == :string
149
+ class_eval "def #{name}; @#{name} ||= redis[field_key('#{name}')]; end"
150
+ class_eval "def #{name}=(value); @#{name} = redis[field_key('#{name}')] = value; end"
151
+ else
152
+ class_eval "def #{name}; @#{name} ||= Marshal::#{class_name}.load(redis[field_key('#{name}')]); end"
153
+ class_eval "def #{name}=(value); @#{name} = value; redis[field_key('#{name}')] = Marshal::#{class_name}.dump(value); end"
154
+ end
155
+ end
156
+ alias_method :value, :field
157
+
158
+ # Defines accessor for redis list
159
+ def list(name, type = :string)
160
+ class_name = marshal_class_name(name, type)
161
+
162
+ fields << {:name => name.to_s, :type => :list}
163
+ class_eval "def #{name}; @#{name} ||= ListProxy.new(self.redis, field_key('#{name}'), Marshal::#{class_name}); end"
164
+ eval_writer(name)
165
+ end
166
+
167
+ # Defines accessor for redis set
168
+ def set(name, type = :string)
169
+ class_name = marshal_class_name(name, type)
170
+
171
+ fields << {:name => name.to_s, :type => :set}
172
+ class_eval "def #{name}; @#{name} ||= SetProxy.new(self.redis, field_key('#{name}'), Marshal::#{class_name}); end"
173
+ eval_writer(name)
174
+ end
175
+
176
+ def marshal_class_name(name, type)
177
+ Marshal::TYPES[type] or raise ArgumentError.new("Unknown type #{type} for field #{name}")
178
+ end
179
+
180
+ # Redefine this to change connection options
181
+ def redis
182
+ @@redis ||= Store.db
183
+ end
184
+
185
+ def fields #:nodoc:
186
+ @fields ||= []
187
+ end
188
+
189
+ protected
190
+ def eval_writer(name) #:nodoc:
191
+ class_eval <<-END
192
+ def #{name}=(value)
193
+ field = self.#{name};
194
+ if value.respond_to?(:each)
195
+ value.each {|v| field << v}
196
+ else
197
+ field << v
198
+ end
199
+ end
200
+ END
201
+ end
202
+
203
+ def populate_model(model, fields)
204
+ return model if fields.empty?
205
+ methods = model.methods
206
+ fields.each do |name,value|
207
+ model.send("#{name}=", value) if methods.include?(name.to_s)
208
+ end
209
+ model
210
+ end
211
+ end
212
+
213
+ module Marshal
214
+ TYPES = {
215
+ :string => 'String',
216
+ :integer => 'Integer',
217
+ :float => 'Float',
218
+ :datetime => 'DateTime',
219
+ :json => 'JSON',
220
+ }
221
+
222
+ class String
223
+ def self.dump(v)
224
+ v
225
+ end
226
+
227
+ def self.load(v)
228
+ v
229
+ end
230
+ end
231
+
232
+ class Integer
233
+ def self.dump(v)
234
+ v.to_s
235
+ end
236
+
237
+ def self.load(v)
238
+ v && v.to_i
239
+ end
240
+ end
241
+
242
+ class Float
243
+ def self.dump(v)
244
+ v.to_s
245
+ end
246
+
247
+ def self.load(v)
248
+ v && v.to_f
249
+ end
250
+ end
251
+
252
+ class DateTime
253
+ def self.dump(v)
254
+ v.strftime('%FT%T%z')
255
+ end
256
+
257
+ def self.load(v)
258
+ v && ::DateTime.strptime(v, '%FT%T%z')
259
+ end
260
+ end
261
+
262
+ class JSON
263
+ def self.dump(v)
264
+ ::JSON.dump(v)
265
+ end
266
+
267
+ def self.load(v)
268
+ v && ::JSON.load(v)
269
+ end
270
+ end
271
+ end
272
+
273
+
274
+
275
+ class FieldProxy #:nodoc
276
+ def initialize(redis, name, marshal)
277
+ @redis = redis
278
+ @name = name
279
+ @marshal = marshal
280
+ end
281
+
282
+ # def method_missing(method, *argv)
283
+ # translated_method = translate_method_name(method)
284
+ # raise NoMethodError.new("Method '#{method}' is not defined") unless translated_method
285
+ # @redis.send translated_method, @name, *argv
286
+ # end
287
+ #
288
+ # protected
289
+ # def translate_method_name(m)
290
+ # m
291
+ # end
292
+ end
293
+
294
+
295
+
296
+ class ListProxy < FieldProxy #:nodoc:
297
+ def <<(v)
298
+ @redis.rpush @name, @marshal.dump(v)
299
+ end
300
+ alias_method :push_tail, :<<
301
+
302
+ def push_head(v)
303
+ @redis.lpush @name, @marshal.dump(v)
304
+ end
305
+
306
+ def pop_tail
307
+ @marshal.load(@redis.rpop(@name))
308
+ end
309
+
310
+ def pop_head
311
+ @marshal.load(@redis.lpop(@name))
312
+ end
313
+
314
+ def [](from, to = nil)
315
+ if to.nil?
316
+ @marshal.load(@redis.lindex(@name, from))
317
+ else
318
+ @redis.lrange(@name, from, to).map! { |v| @marshal.load(v) }
319
+ end
320
+ end
321
+ alias_method :range, :[]
322
+
323
+ def []=(index, v)
324
+ @redis.lset(@name, index, @marshal.dump(v))
325
+ end
326
+ alias_method :set, :[]=
327
+
328
+ def include?(v)
329
+ @redis.exists(@name, @marshal.dump(v))
330
+ end
331
+
332
+ def remove(count, v)
333
+ @redis.lrem(@name, count, @marshal.dump(v))
334
+ end
335
+
336
+ # def length
337
+ # @redis.llen(@name)
338
+ # end
339
+ #
340
+ # def trim(from, to)
341
+ # @redis.ltrim(@name, from, to)
342
+ # end
343
+ #
344
+ # def to_s
345
+ # range(0, 100).join(', ')
346
+ # end
347
+ #
348
+ # protected
349
+ # def translate_method_name(m)
350
+ # COMMANDS[m]
351
+ # end
352
+ end
353
+
354
+
355
+
356
+ class SetProxy < FieldProxy #:nodoc:
357
+ # COMMANDS = {
358
+ # :intersect_store => "sinterstore",
359
+ # :union_store => "sunionstore",
360
+ # :diff_store => "sdiffstore",
361
+ # :move => "smove",
362
+ # }
363
+
364
+ def <<(v)
365
+ @redis.sadd @name, @marshal.dump(v)
366
+ end
367
+ alias_method :add, :<<
368
+
369
+ def delete(v)
370
+ @redis.srem @name, @marshal.dump(v)
371
+ end
372
+ alias_method :remove, :delete
373
+
374
+ def include?(v)
375
+ @redis.sismember @name, @marshal.dump(v)
376
+ end
377
+ alias_method :has_key?, :include?
378
+ alias_method :member?, :include?
379
+
380
+ def members
381
+ @redis.smembers(@name).map { |v| @marshal.load(v) }
382
+ end
383
+
384
+ def intersect(*keys)
385
+ @redis.sinter(@name, *keys).map { |v| @marshal.load(v) }
386
+ end
387
+
388
+ def union(*keys)
389
+ @redis.sunion(@name, *keys).map { |v| @marshal.load(v) }
390
+ end
391
+
392
+ def diff(*keys)
393
+ @redis.sdiff(@name, *keys).map { |v| @marshal.load(v) }
394
+ end
395
+
396
+ # def length
397
+ # @redis.llen(@name)
398
+ # end
399
+ #
400
+ # def to_s
401
+ # members.join(', ')
402
+ # end
403
+ #
404
+ # protected
405
+ # def translate_method_name(m)
406
+ # COMMANDS[m]
407
+ # end
408
+ end
409
+ end
410
+ end
@@ -0,0 +1,55 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ module Rhosync
4
+ class Blackberry
5
+ def self.ping(params)
6
+ begin
7
+ settings = get_config(Rhosync.base_directory)[Rhosync.environment]
8
+ host = settings[:mdsserver]
9
+ port = settings[:mdsserverport]
10
+
11
+ headers = { "X-WAP-APPLICATION-ID" => "/",
12
+ "X-RIM-PUSH-DEST-PORT" => params['device_port'],
13
+ "CONTENT-TYPE" => 'multipart/related; type="application/xml"; boundary=asdlfkjiurwghasf'}
14
+
15
+ Net::HTTP.new(host,port).start do |http|
16
+ request = Net::HTTP::Post.new('/pap',headers)
17
+ request.body = pap_message(params)
18
+ http.request(request)
19
+ end
20
+
21
+ rescue Exception => error
22
+ log "Error while sending ping: #{error}"
23
+ raise error
24
+ end
25
+ end
26
+
27
+ def self.pap_message(params)
28
+ data = "do_sync=" + (params['sources'] ? params['sources'].join(',') : "") + "\r\n"
29
+ data << "show_popup=#{params['message']}\r\n" if params['message']
30
+ data << "vibrate=#{params['vibrate']}\r\n" if params['vibrate']
31
+ post_body = <<-DESC
32
+ --asdlfkjiurwghasf
33
+ Content-Type: application/xml; charset=UTF-8
34
+
35
+ <?xml version="1.0"?>
36
+ <!DOCTYPE pap PUBLIC "-//WAPFORUM//DTD PAP 2.0//EN"
37
+ "http://www.wapforum.org/DTD/pap_2.0.dtd"
38
+ [<?wap-pap-ver supported-versions="2.0"?>]>
39
+ <pap>
40
+ <push-message push-id="pushID:#{(rand * 100000000).to_i.to_s}" ppg-notify-requested-to="http://localhost:7778">
41
+
42
+ <address address-value="WAPPUSH=#{params['device_pin'].to_i.to_s(base=16).upcase}%3A100/TYPE=USER@rim.net"/>
43
+ <quality-of-service delivery-method="preferconfirmed"/>
44
+ </push-message>
45
+ </pap>
46
+ --asdlfkjiurwghasf
47
+ Content-Type: text/plain
48
+
49
+ #{data}
50
+ --asdlfkjiurwghasf--
51
+ DESC
52
+ post_body.gsub!(/\n/,"\r\n")
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,44 @@
1
+ require 'socket'
2
+ require 'openssl'
3
+ module Rhosync
4
+ class Iphone
5
+ def self.ping(params)
6
+ settings = get_config(Rhosync.base_directory)[Rhosync.environment]
7
+ cert_file = File.join(Rhosync.base_directory,settings[:iphonecertfile])
8
+ cert = File.read(cert_file) if File.exists?(cert_file)
9
+ passphrase = settings[:iphonepassphrase]
10
+ host = settings[:iphoneserver]
11
+ port = settings[:iphoneport]
12
+ begin
13
+ ssl_ctx = OpenSSL::SSL::SSLContext.new
14
+ ssl_ctx.key = OpenSSL::PKey::RSA.new(cert, passphrase)
15
+ ssl_ctx.cert = OpenSSL::X509::Certificate.new(cert)
16
+
17
+ socket = TCPSocket.new(host, port)
18
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_ctx)
19
+ ssl_socket.sync = true
20
+ ssl_socket.connect
21
+
22
+ ssl_socket.write(apn_message(params))
23
+ ssl_socket.close
24
+ socket.close
25
+ rescue SocketError => error
26
+ log "Error while sending ping: #{error}"
27
+ raise error
28
+ end
29
+ end
30
+
31
+ # Generates APNS package
32
+ def self.apn_message(params)
33
+ data = {}
34
+ data['aps'] = {}
35
+ data['aps']['alert'] = params['message'] if params['message']
36
+ data['aps']['badge'] = params['badge'].to_i if params['badge']
37
+ data['aps']['sound'] = params['sound'] if params['sound']
38
+ data['aps']['vibrate'] = params['vibrate'] if params['vibrate']
39
+ data['do_sync'] = params['sources'] if params['sources']
40
+ json = data.to_json
41
+ "\0\0 #{[params['device_pin'].delete(' ')].pack('H*')}\0#{json.length.chr}#{json}"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,2 @@
1
+ require 'rhosync/ping/iphone'
2
+ require 'rhosync/ping/blackberry'
@@ -0,0 +1,27 @@
1
+ module Rhosync
2
+ class ReadState < Model
3
+ field :refresh_time,:integer
4
+
5
+ def self.create(fields)
6
+ fields[:id] = get_id(fields)
7
+ fields.delete(:app_id)
8
+ fields.delete(:user_id)
9
+ fields.delete(:source_name)
10
+ fields[:refresh_time] ||= Time.now.to_i
11
+ super(fields,{})
12
+ end
13
+
14
+ def self.load(params)
15
+ super(get_id(params),{})
16
+ end
17
+
18
+ def self.delete(app_id)
19
+ Store.flash_data("#{class_prefix(self)}:#{app_id}:*")
20
+ end
21
+
22
+ private
23
+ def self.get_id(params)
24
+ "#{params[:app_id]}:#{params[:user_id]}:#{params[:source_name]}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <head>
3
+ <title>Rhosync Server</title>
4
+ </head>
5
+ <body>
6
+ <h3>Rhosync Server v<%=Rhosync::VERSION%> running...</h3>
7
+ <p><a href="/resque/">Resque</a></p>
8
+ <p><a href="/console/">Console</a></p>
9
+ <h3>Rhosync Info</h3>
10
+ TBD...
11
+ </body>
12
+ </html>