rhosync 2.0.8 → 2.0.9

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 (56) hide show
  1. data/CHANGELOG +9 -0
  2. data/Rakefile +1 -5
  3. data/doc/protocol.html +397 -156
  4. data/lib/rhosync/console/app/public/ThickBox.css +649 -0
  5. data/lib/rhosync/console/app/public/home.css +433 -0
  6. data/lib/rhosync/console/app/public/images/header_halo copy.jpg +0 -0
  7. data/lib/rhosync/console/app/public/images/header_halo.jpg +0 -0
  8. data/lib/rhosync/console/app/public/images/land_separator.gif +0 -0
  9. data/lib/rhosync/console/app/public/images/landing_header.jpg +0 -0
  10. data/lib/rhosync/console/app/public/images/logo_rhosync.png +0 -0
  11. data/lib/rhosync/console/app/public/images/rhomobile_rhohub_logo.png +0 -0
  12. data/lib/rhosync/console/app/public/images/tabs_separator.png +0 -0
  13. data/lib/rhosync/console/app/public/reset.css +76 -0
  14. data/lib/rhosync/console/app/public/style.css +2201 -0
  15. data/lib/rhosync/console/app/routes/auth.rb +1 -1
  16. data/lib/rhosync/console/app/routes/client.rb +1 -1
  17. data/lib/rhosync/console/app/routes/docs.rb +98 -55
  18. data/lib/rhosync/console/app/routes/home.rb +47 -8
  19. data/lib/rhosync/console/app/routes/timing.rb +26 -0
  20. data/lib/rhosync/console/app/routes/user.rb +58 -21
  21. data/lib/rhosync/console/app/views/client.erb +2 -2
  22. data/lib/rhosync/console/app/views/content.erb +14 -0
  23. data/lib/rhosync/console/app/views/docdata.erb +1 -1
  24. data/lib/rhosync/console/app/views/docs.erb +2 -2
  25. data/lib/rhosync/console/app/views/headermenu.erb +41 -0
  26. data/lib/rhosync/console/app/views/home.erb +22 -0
  27. data/lib/rhosync/console/app/views/index.erb +1 -1
  28. data/lib/rhosync/console/app/views/layout.erb +82 -7
  29. data/lib/rhosync/console/app/views/login.erb +26 -0
  30. data/lib/rhosync/console/app/views/newuser.erb +1 -1
  31. data/lib/rhosync/console/app/views/result.erb +1 -1
  32. data/lib/rhosync/console/app/views/rightboxlinks.erb +15 -0
  33. data/lib/rhosync/console/app/views/select_doc.erb +1 -3
  34. data/lib/rhosync/console/app/views/user.erb +3 -6
  35. data/lib/rhosync/console/app/views/users.erb +2 -4
  36. data/lib/rhosync/console/server.rb +6 -0
  37. data/lib/rhosync/jobs/bulk_data_job.rb +17 -4
  38. data/lib/rhosync/ping/android.rb +38 -0
  39. data/lib/rhosync/ping.rb +2 -1
  40. data/lib/rhosync/server/views/index.erb +3 -0
  41. data/lib/rhosync/server.rb +25 -6
  42. data/lib/rhosync/source_sync.rb +14 -6
  43. data/lib/rhosync/stats/middleware.rb +22 -0
  44. data/lib/rhosync/stats/record.rb +83 -0
  45. data/lib/rhosync/version.rb +1 -1
  46. data/lib/rhosync.rb +4 -2
  47. data/spec/ping/android_spec.rb +45 -0
  48. data/spec/rhosync_spec.rb +1 -0
  49. data/spec/server/server_spec.rb +8 -0
  50. data/spec/spec.opts +3 -0
  51. data/spec/spec_helper.rb +4 -3
  52. data/spec/stats/middleware_spec.rb +32 -0
  53. data/spec/stats/record_spec.rb +71 -0
  54. metadata +31 -7
  55. data/lib/rhosync/monitoring/record.rb +0 -28
  56. data/spec/monitoring/record_spec.rb +0 -21
@@ -7,5 +7,5 @@
7
7
  <%end%>
8
8
  </div>
9
9
  <div class='panel'>
10
- <a href='<%= @back_href%>' class='nav_button'>Back</a>
10
+ <a href='javascript:void(0);' onclick="loadXMLDoc('<%= @back_href%>','main_box');" class='nav_button'>Back</a>
11
11
  </div>
@@ -0,0 +1,15 @@
1
+ <!-- div, links[]{ :url, :title, :selected }> -->
2
+
3
+ <div id="right-box_links">
4
+ <ul class="right-box_links">
5
+ <% i=1 %>
6
+ <% links.each do |link| %>
7
+ <li><a id="link<%=i%>"
8
+ <%= 'class="selected"' if link[:selected] %>
9
+ onclick="javascript:loadXMLDoc('<%=link[:url]%>','<%= div %>');switch_tab(this.id); this.blur();"
10
+ href="javascript:void(0);"><%=link[:title]%></a>
11
+ </li>
12
+ <% i = i + 1 %>
13
+ <% end %>
14
+ </ul>
15
+ </div>
@@ -1,8 +1,6 @@
1
1
  <h1>Document <%= @dbkey%></h1>
2
2
  <%=show_errors%>
3
- <div class='panel'>
4
- <a href='<%= url('/')%>' class='nav_button'>Back</a>
5
- </div>
3
+
6
4
  <form action=<%= url('doc/select')%> method='GET'>
7
5
  <table>
8
6
  <tr>
@@ -1,8 +1,5 @@
1
1
  <h1>User: <%=params[:user_id]%></h1>
2
2
  <%=show_errors%>
3
- <div class="panel">
4
- <a href="<%=url('users')%>" class='nav_button'>Back</a>
5
- </div>
6
3
  <div class="panel">
7
4
  <a href='<%=url("user/delete?user_id=#{CGI.escape(params[:user_id])}")%>' class='nav_button'>Delete user</a>
8
5
  </div>
@@ -13,18 +10,18 @@
13
10
 
14
11
  [
15
12
  <%@sources.each_index do |i|%>
16
- <a href="<%=url("docs?source_id=#{@sources[i]}&user_id=#{CGI.escape(params[:user_id])}")%>"><%=i==0?'':','%>"<%=@sources[i]%>"</a>
13
+ <a href='javascript:void(0);' onclick="loadXMLDoc('<%=url("docs?source_id=#{@sources[i]}&user_id=#{CGI.escape(params[:user_id])}")%>','main_box');"><%=i==0?'':','%>"<%=@sources[i]%>"</a>
17
14
  <%end%>
18
15
  ]
19
16
 
20
17
  <h2>Registered Devices</h2>
21
18
 
22
- <a href="<%=url("device/create?user_id=#{CGI.escape(params[:user_id])}")%>" class='nav_button'>Create Device</a>
19
+ <a href="javascript:void(0);" onclick="loadXMLDoc('<%=url("device/create?user_id=#{CGI.escape(params[:user_id])}")%>','');loadXMLDoc('<%=url("user?user_id=#{CGI.escape(params[:user_id])}")%>','main_box');" class='nav_button'>Create Device</a>
23
20
 
24
21
  <div class="panel">
25
22
  [
26
23
  <%@devices.each_index do |i|%>
27
- <a href="<%=url("device?user_id=#{CGI.escape(params[:user_id])}&device_id=#{CGI.escape(@devices[i])}")%>">
24
+ <a href="javascript:void(0);" onclick="loadXMLDoc('<%=url("device?user_id=#{CGI.escape(params[:user_id])}&device_id=#{CGI.escape(@devices[i])}")%>','main_box');">
28
25
  <%=i==0?'':','%>"<%=@devices[i]%>"
29
26
  </a>
30
27
  <%end%>
@@ -1,6 +1,4 @@
1
- <h1>Users</h1>
2
- <a href="<%=url('/')%>" class='nav_button'>Back</a>
3
- <a href="<%=url('user/new')%>" class='nav_button'>Create User</a>
1
+ <a href="javascript:void(0);" onclick="loadXMLDoc('<%=url('user/new')%>','main_box');" class='nav_button'>Create User</a>
4
2
 
5
3
  <h2>Registered users</h2>
6
4
  <%if is_errors?%>
@@ -8,7 +6,7 @@
8
6
  <%else%>
9
7
  [
10
8
  <%@users.each_index do |i|%>
11
- <a href="<%=url("user?user_id=#{CGI.escape(@users[i])}")%>"><%=i==0?'':','%>"<%=@users[i]%>"</a>
9
+ <a href="javascript:void(0);" onclick="loadXMLDoc('<%=url("user?user_id=#{CGI.escape(@users[i])}")%>','main_box');"><%=i==0?'':','%>"<%=@users[i]%>"</a>
12
10
  <%end%>
13
11
  ]
14
12
  <%end%>
@@ -19,6 +19,12 @@ module RhosyncConsole
19
19
  set :public, RhosyncConsole::root_path("app","public")
20
20
  set :static, true
21
21
  use Rack::Session::Cookie
22
+ before do
23
+ headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT'
24
+ headers['Cache-Control'] = 'no-store, no-cache, must-revalidate'
25
+ headers['Pramga'] = 'no-cache'
26
+ end
27
+
22
28
  end
23
29
  end
24
30
 
@@ -16,6 +16,7 @@ module Rhosync
16
16
  ts = Time.now.to_i.to_s
17
17
  create_sqlite_data_file(bulk_data,ts)
18
18
  timer = lap_timer('create_sqlite_data_file',timer)
19
+ log " bulk_data.dbfile : #{bulk_data.dbfile}"
19
20
  create_hsql_data_file(bulk_data,ts) if Rhosync.blackberry_bulk_sync
20
21
  lap_timer('create_hsql_data_file',timer)
21
22
  log "finished bulk data process"
@@ -54,13 +55,13 @@ module Rhosync
54
55
  data = source.get_data(:md)
55
56
  counter = {}
56
57
  columns,qm = [],[]
57
- create_table = ['object varchar']
58
+ create_table = ["\"object\" varchar"]
58
59
  schema = JSON.parse(source.schema)
59
60
 
60
61
  db.transaction do |database|
61
62
  # Create a table with columns specified by 'property' array in settings
62
63
  schema['property'].each do |key,value|
63
- create_table << "#{key} varchar default NULL"
64
+ create_table << "\"#{key}\" varchar default NULL"
64
65
  columns << key
65
66
  qm << '?'
66
67
  end
@@ -81,12 +82,24 @@ module Rhosync
81
82
 
82
83
  # Create indexes for specified columns in settings 'index'
83
84
  schema['index'].each do |key,value|
84
- database.execute("CREATE INDEX #{key} on #{source.name} (#{value});")
85
+ val2 = ""
86
+ value.split(',').each do |col|
87
+ val2 += ',' if val2.length() > 0
88
+ val2 += "\"#{col}\""
89
+ end
90
+
91
+ database.execute("CREATE INDEX #{key} on #{source.name} (#{val2});")
85
92
  end if schema['index']
86
93
 
87
94
  # Create unique indexes for specified columns in settings 'unique_index'
88
95
  schema['unique_index'].each do |key,value|
89
- database.execute("CREATE UNIQUE INDEX #{key} on #{source.name} (#{value});")
96
+ val2 = ""
97
+ value.split(',').each do |col|
98
+ val2 += ',' if val2.length() > 0
99
+ val2 += "\"#{col}\""
100
+ end
101
+
102
+ database.execute("CREATE UNIQUE INDEX #{key} on #{source.name} (#{val2});")
90
103
  end if schema['unique_index']
91
104
  end
92
105
 
@@ -0,0 +1,38 @@
1
+ require 'net/https'
2
+ require 'uri'
3
+ module Rhosync
4
+ class Android
5
+ def self.ping(params)
6
+ begin
7
+ settings = get_config(Rhosync.base_directory)[Rhosync.environment]
8
+ authtoken = settings[:authtoken]
9
+
10
+ url = URI.parse('https://android.apis.google.com/c2dm/send')
11
+
12
+ req = Net::HTTP::Post.new url.path, 'Authorization' => "GoogleLogin auth=#{authtoken}"
13
+ req.set_form_data c2d_message(params)
14
+
15
+ http = Net::HTTP.new(url.host, url.port)
16
+ http.use_ssl = true
17
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
18
+ res = http.request(req)
19
+
20
+ rescue Exception => error
21
+ log "Error while sending ping: #{error}"
22
+ raise error
23
+ end
24
+ end
25
+
26
+ def self.c2d_message(params)
27
+ data = {}
28
+ data['registration_id'] = params['device_pin']
29
+ data['collapse_key'] = (rand * 100000000).to_i.to_s
30
+ data['data.do_sync'] = params['sources'] ? params['sources'].join(',') : ''
31
+ data['data.alert'] = params['message'] if params['message']
32
+ data['data.vibrate'] = params['vibrate'] if params['vibrate']
33
+ data['data.sound'] = params['sound'] if params['sound']
34
+
35
+ data
36
+ end
37
+ end
38
+ end
data/lib/rhosync/ping.rb CHANGED
@@ -1,2 +1,3 @@
1
+ require 'rhosync/ping/android'
1
2
  require 'rhosync/ping/apple'
2
- require 'rhosync/ping/blackberry'
3
+ require 'rhosync/ping/blackberry'
@@ -1,5 +1,8 @@
1
1
  <html>
2
2
  <head>
3
+ <script type="text/javascript">
4
+ window.location="/console/";
5
+ </script>
3
6
  <title>Rhosync Server</title>
4
7
  </head>
5
8
  <body>
@@ -21,12 +21,12 @@ module Rhosync
21
21
  set :public, "#{libdir}/server/public"
22
22
  set :static, true
23
23
 
24
- set :secret, '<changeme>' unless defined? Server.secret
24
+ # default secret
25
+ @@secret = '<changeme>'
25
26
 
26
- use Rack::Session::Cookie, :key => 'rhosync_session',
27
- :expire_after => 31536000,
28
- :secret => Server.secret
29
-
27
+ # stats middleware disabled by default
28
+ @@stats = false
29
+
30
30
  # Setup route and mimetype for bulk data downloads
31
31
  # TODO: Figure out why "mime :data, 'application/octet-stream'" doesn't work
32
32
  Rack::Mime::MIME_TYPES['.data'] = 'application/octet-stream'
@@ -126,10 +126,29 @@ module Rhosync
126
126
  end
127
127
  end
128
128
  end
129
+
130
+ # hook into new so we can enable middleware
131
+ def self.new
132
+ if @@stats == true
133
+ use Rhosync::Stats::Middleware
134
+ Rhosync.stats = true
135
+ end
136
+ use Rack::Session::Cookie,
137
+ :key => 'rhosync_session',
138
+ :expire_after => 31536000,
139
+ :secret => @@secret
140
+ super
141
+ end
142
+
143
+ def self.set(option, value=self, &block)
144
+ @@stats = value if option == :stats and (value.is_a?(TrueClass) or value.is_a?(FalseClass))
145
+ @@secret = value if option == :secret and value.is_a?(String)
146
+ super
147
+ end
129
148
 
130
149
  def initialize
131
150
  # Whine about default session secret
132
- check_default_secret!(Server.secret)
151
+ check_default_secret!(@@secret)
133
152
  super
134
153
  end
135
154
 
@@ -11,15 +11,15 @@ module Rhosync
11
11
 
12
12
  # CUD Operations
13
13
  def create(client_id)
14
- _process_cud('create',client_id)
14
+ _measure_and_process_cud('create',client_id)
15
15
  end
16
16
 
17
17
  def update(client_id)
18
- _process_cud('update',client_id)
18
+ _measure_and_process_cud('update',client_id)
19
19
  end
20
20
 
21
21
  def delete(client_id)
22
- _process_cud('delete',client_id)
22
+ _measure_and_process_cud('delete',client_id)
23
23
  end
24
24
 
25
25
  # Read Operation; params are query arguments
@@ -60,9 +60,11 @@ module Rhosync
60
60
 
61
61
  def do_query(params=nil)
62
62
  @source.if_need_refresh do
63
- return if _auth_op('login') == false
64
- self.read(nil,params)
65
- _auth_op('logoff')
63
+ Stats::Record.update("source:query:#{@source.name}") do
64
+ return if _auth_op('login') == false
65
+ self.read(nil,params)
66
+ _auth_op('logoff')
67
+ end
66
68
  end
67
69
  end
68
70
 
@@ -143,6 +145,12 @@ module Rhosync
143
145
  dels[key] = value
144
146
  end
145
147
 
148
+ def _measure_and_process_cud(operation,client_id)
149
+ Stats::Record.update("source:#{operation}:#{@source.name}") do
150
+ _process_cud(operation,client_id)
151
+ end
152
+ end
153
+
146
154
  def _process_cud(operation,client_id)
147
155
  errors,links,deletes,creates,dels = {},{},{},{},{}
148
156
  client = Client.load(client_id,{:source_name => @source.name})
@@ -0,0 +1,22 @@
1
+ module Rhosync
2
+ module Stats
3
+ class Middleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ start = Time.now.to_f
10
+ status, headers, body = @app.call(env)
11
+ finish = Time.now.to_f
12
+ metric = "http:#{env['REQUEST_METHOD']}:#{env['REQUEST_PATH']}"
13
+ source_id = env['rack.request.query_hash']["source_id"] if env['rack.request.query_hash']
14
+ metric << ":#{source_id}" if source_id
15
+ Record.add(metric,finish - start) do |counter,aggregate|
16
+ Record.save_average(counter,aggregate)
17
+ end
18
+ [status, headers, body]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,83 @@
1
+ module Rhosync
2
+ module Stats
3
+ class Record
4
+ class << self
5
+
6
+ # Add a value to a metric. If zset already has a member,
7
+ # update the existing member with an incremented value by default.
8
+ # Also supports updating the value with a block (useful for averages)
9
+ def add(metric, value = 1)
10
+ start = Time.now.to_i
11
+ current, current_score = 0, start
12
+ range = Store.db.zrevrange(key(metric), 0, 0)
13
+ if !range.empty?
14
+ member = range[0]
15
+ m_current = member.split(':')[0]
16
+ m_current_score = Store.db.zscore(key(metric), member).to_i
17
+ if m_current_score > (start - resolution(metric))
18
+ Store.db.zrem(key(metric), member)
19
+ current, current_score = m_current, m_current_score
20
+ end
21
+ end
22
+ value = block_given? ? yield(current, value) : (current.to_i + value)
23
+ Store.db.zadd(key(metric), current_score, "#{value}:#{start}")
24
+ Store.db.zremrangebyscore(key(metric), 0, start - record_size(metric))
25
+ end
26
+
27
+ # Saves the accumulated average for a resolution in a metric
28
+ def save_average(current, value)
29
+ sum = value
30
+ if current.is_a?(String)
31
+ current,sum = current.split(',')
32
+ current = current.to_f
33
+ sum = sum.to_f+value
34
+ end
35
+ "#{current + 1},#{sum}"
36
+ end
37
+
38
+ def update(metric)
39
+ if Rhosync.stats
40
+ start = Time.now.to_f
41
+ # perform the operations
42
+ yield
43
+ finish = Time.now.to_f
44
+ add(metric, finish - start) do |counter, aggregate|
45
+ save_average(counter, aggregate)
46
+ end
47
+ else
48
+ yield
49
+ end
50
+ end
51
+
52
+ def reset(metric)
53
+ Store.db.del(key(metric))
54
+ end
55
+
56
+ def reset_all
57
+ Store.flash_data('stat:*')
58
+ end
59
+
60
+ # Returns the metric data, uses array indexing
61
+ def range(metric, start, finish = -1)
62
+ Store.db.zrange(key(metric), start, finish)
63
+ end
64
+
65
+ # Returns the resolution for a given metric, default 60 seconds
66
+ def resolution(metric)
67
+ resolution = Object.const_get("#{metric.upcase}_RECORD_RESOLUTION") rescue nil
68
+ resolution || 60 #=> 1 minute aggregate
69
+ end
70
+
71
+ # Returns the # of records to save for a given metric
72
+ def record_size(metric)
73
+ size = Object.const_get("#{metric.upcase}_RECORD_SIZE") rescue nil
74
+ size || 60 * 24 * 31 #=> 44640 minutes
75
+ end
76
+
77
+ def key(metric)
78
+ "stat:#{metric}"
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -1,3 +1,3 @@
1
1
  module Rhosync
2
- VERSION = '2.0.8'
2
+ VERSION = '2.0.9'
3
3
  end
data/lib/rhosync.rb CHANGED
@@ -21,7 +21,8 @@ require 'rhosync/source_sync'
21
21
  require 'rhosync/rho_indifferent_access'
22
22
  require 'rhosync/jobs/source_job'
23
23
  require 'rhosync/jobs/ping_job'
24
- require 'rhosync/monitoring/record'
24
+ require 'rhosync/stats/record'
25
+ require 'rhosync/stats/middleware'
25
26
  require 'rhosync/bulk_data'
26
27
 
27
28
  REDIS_URL = 'REDIS' unless defined? REDIS_URL
@@ -41,7 +42,7 @@ module Rhosync
41
42
  class << self
42
43
  attr_accessor :base_directory, :app_directory, :data_directory,
43
44
  :vendor_directory, :blackberry_bulk_sync, :redis, :environment,
44
- :log_disabled, :license, :bulk_sync_poll_interval
45
+ :log_disabled, :license, :bulk_sync_poll_interval, :stats
45
46
  end
46
47
 
47
48
  ### Begin Rhosync setup methods
@@ -70,6 +71,7 @@ module Rhosync
70
71
  Rhosync.blackberry_bulk_sync ||= false
71
72
  Rhosync.bulk_sync_poll_interval ||= 3600
72
73
  Rhosync.log_disabled ||= false
74
+ Rhosync.stats ||= false
73
75
  Rhosync.license = License.new
74
76
 
75
77
  check_and_add(File.join(Rhosync.app_directory,'sources'))
@@ -0,0 +1,45 @@
1
+ require File.join(File.dirname(__FILE__),'..','spec_helper')
2
+
3
+ describe "Ping Android" do
4
+ it_should_behave_like "SpecBootstrapHelper"
5
+ it_should_behave_like "SourceAdapterHelper"
6
+
7
+ before do
8
+ @params = {"device_pin" => @c.device_pin,
9
+ "sources" => [@s.name], "message" => 'hello world',
10
+ "vibrate" => '5', "badge" => '5', "sound" => 'hello.mp3'}
11
+ post = mock('post')
12
+ post.stub!(:new).and_return(post)
13
+ post.stub!(:set_form_data)
14
+ Net::HTTP::Post.stub!(:new).and_return(post)
15
+
16
+ @http = mock('http')
17
+ @http.stub!(:request)
18
+ @http.stub!(:use_ssl=)
19
+ @http.stub!(:verify_mode=)
20
+ @http.stub!(:start).and_yield(@http)
21
+ Net::HTTP.stub!(:new).and_return(@http)
22
+ end
23
+
24
+ it "should ping android" do
25
+ Android.ping(@params)
26
+ end
27
+
28
+ it "should ping android with connection error" do
29
+ error = 'Connection refused'
30
+ @http.stub!(:request).and_return { raise SocketError.new(error) }
31
+ Android.should_receive(:log).once.with("Error while sending ping: #{error}")
32
+ lambda { Android.ping(@params) }.should raise_error(SocketError,error)
33
+ end
34
+
35
+ it "should compute c2d_message" do
36
+ expected = {'registration_id' => @c.device_pin, 'collapse_key' => "RAND_KEY",
37
+ 'data.do_sync' => @s.name,
38
+ 'data.alert' => "hello world",
39
+ 'data.vibrate' => '5',
40
+ 'data.sound' => "hello.mp3"}
41
+ actual = Android.c2d_message(@params)
42
+ actual['collapse_key'] = "RAND_KEY" unless actual['collapse_key'].nil?
43
+ actual.should == expected
44
+ end
45
+ end
data/spec/rhosync_spec.rb CHANGED
@@ -16,6 +16,7 @@ describe "Rhosync" do
16
16
  Rhosync.blackberry_bulk_sync.should == false
17
17
  Rhosync.bulk_sync_poll_interval.should == 3600
18
18
  Rhosync.environment.should == :development
19
+ Rhosync.stats.should == false
19
20
  App.is_exist?(@test_app_name).should be_true
20
21
  end
21
22
 
@@ -57,6 +57,14 @@ describe "Server" do
57
57
  Rhosync::Server.secret.should == "secure!"
58
58
  end
59
59
 
60
+ it "should use Stats::Middleware if stats enabled" do
61
+ Rhosync::Server.enable :stats
62
+ Rhosync::Server.new
63
+ Rhosync.stats.should == true
64
+ Rhosync.stats = nil
65
+ Rhosync::Server.disable :stats
66
+ end
67
+
60
68
  it "should update session secret to default" do
61
69
  Rhosync::Server.set :secret, "<changeme>"
62
70
  Rhosync::Server.secret.should == "<changeme>"
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ -fn
3
+ -b
data/spec/spec_helper.rb CHANGED
@@ -117,9 +117,9 @@ module TestHelpers
117
117
  db.execute("select source_id,name,sync_priority,partition,
118
118
  sync_type,source_attribs,metadata,blob_attribs,associations
119
119
  from sources where name='#{s.name}'").each do |row|
120
- return false if row[0] != s.source_id.to_s
120
+ return false if row[0].to_s != s.source_id.to_s
121
121
  return false if row[1] != s.name
122
- return false if row[2] != s.priority.to_s
122
+ return false if row[2].to_s != s.priority.to_s
123
123
  return false if row[3] != s.partition_type.to_s
124
124
  return false if row[4] != s.sync_type.to_s
125
125
  return false if row[5] != (s.schema ? "" : get_attrib_counter(data))
@@ -127,6 +127,7 @@ module TestHelpers
127
127
  return false if row[7] != s.blob_attribs
128
128
  return false if row[8] != s.has_many
129
129
  end
130
+
130
131
  data = json_clone(data)
131
132
  if s.schema
132
133
  schema = JSON.parse(s.schema)
@@ -145,7 +146,7 @@ module TestHelpers
145
146
  else
146
147
  db.execute("select * from object_values where source_id=#{s.source_id}").each do |row|
147
148
  object = data[row[2]]
148
- return false if object.nil? or object[row[1]] != row[3] or row[0] != s.source_id.to_s
149
+ return false if object.nil? or object[row[1]] != row[3] or row[0].to_s != s.source_id.to_s
149
150
  object.delete(row[1])
150
151
  data.delete(row[2]) if object.empty?
151
152
  end
@@ -0,0 +1,32 @@
1
+ require 'rhosync'
2
+ FOO_RECORD_RESOLUTION = 2
3
+ FOO_RECORD_SIZE = 8
4
+
5
+ include Rhosync
6
+ include Rhosync::Stats
7
+
8
+ describe "Middleware" do
9
+
10
+ before(:each) do
11
+ @now = 10.0
12
+ Store.db.flushdb
13
+ app = mock('app')
14
+ app.stub!(:call)
15
+ @middleware = Middleware.new(app)
16
+ end
17
+
18
+ it "should compute http average" do
19
+ Time.stub!(:now).and_return { @now += 0.3; @now }
20
+ env = {
21
+ 'rack.request.query_hash' => {
22
+ 'source_id' => 'SampleAdapter'
23
+ },
24
+ 'REQUEST_METHOD' => 'GET',
25
+ 'REQUEST_PATH' => '/application'
26
+ }
27
+ 10.times { @middleware.call(env) }
28
+ metric = 'http:GET:/application:SampleAdapter'
29
+ Record.key(metric).should == "stat:#{metric}"
30
+ Record.range(metric, 0, -1).should == ["10.0,3.0:19"]
31
+ end
32
+ end
@@ -0,0 +1,71 @@
1
+ require 'rhosync'
2
+ FOO_RECORD_RESOLUTION = 2
3
+ FOO_RECORD_SIZE = 8
4
+
5
+ include Rhosync
6
+ include Rhosync::Stats
7
+
8
+ describe "Record" do
9
+
10
+ before(:each) do
11
+ @now = 9
12
+ Store.db.flushdb
13
+ end
14
+
15
+ it "should add metric to the record and trim record size" do
16
+ Time.stub!(:now).and_return { @now += 1; @now }
17
+ 10.times { Record.add('foo') }
18
+ Store.db.zrange('stat:foo', 0, -1).should == ["2:13", "2:15", "2:17", "2:19"]
19
+ end
20
+
21
+ it "should add single record" do
22
+ Time.stub!(:now).and_return { @now += 1; @now }
23
+ Record.add('foo')
24
+ Store.db.zrange('stat:foo', 0, -1).should == ["1:10"]
25
+ end
26
+
27
+ it "should add absolute metric value" do
28
+ Time.stub!(:now).and_return { @now += 1; @now }
29
+ time = 0
30
+ 4.times do
31
+ Record.add('foo',time) do |current,value|
32
+ Record.save_average(current, value)
33
+ end
34
+ time += 1
35
+ end
36
+ Store.db.zrange('stat:foo', 0, -1).should == ["2.0,1.0:11", "2.0,5.0:13"]
37
+ end
38
+
39
+ it "should update metric" do
40
+ Rhosync.stats = true
41
+ Time.stub!(:now).and_return { @now += 1; @now }
42
+ 4.times do
43
+ Record.update('foo') do
44
+ # something interesting
45
+ end
46
+ end
47
+ Store.db.zrange('stat:foo', 0, -1).should == ["1,1.0:15", "1,1.0:18", "1,1.0:21"]
48
+ Rhosync.stats = nil
49
+ end
50
+
51
+ it "should get range of metric values" do
52
+ Time.stub!(:now).and_return { @now += 1; @now }
53
+ 10.times { Record.add('foo') }
54
+ Record.range('foo', 0, 1).should == ["2:13", "2:15"]
55
+ end
56
+
57
+ it "should reset metric" do
58
+ Time.stub!(:now).and_return { @now += 1; @now }
59
+ 10.times { Record.add('foo') }
60
+ Store.db.zrange('stat:foo', 0, -1).should == ["2:13", "2:15", "2:17", "2:19"]
61
+ Record.reset('foo')
62
+ Store.db.zrange('stat:foo', 0, -1).should == []
63
+ end
64
+
65
+ it "should reset all metrics" do
66
+ Record.add('foo')
67
+ Record.add('bar')
68
+ Record.reset_all
69
+ Store.db.keys('stat:*').should == []
70
+ end
71
+ end