liveresource 2.0.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 (54) hide show
  1. data/.gitignore +1 -0
  2. data/BSDL +24 -0
  3. data/COPYING +59 -0
  4. data/GPL +339 -0
  5. data/README.md +289 -0
  6. data/Rakefile +47 -0
  7. data/benchmark/benchmark_helper.rb +19 -0
  8. data/benchmark/method_benchmark.rb +94 -0
  9. data/lib/live_resource.rb +66 -0
  10. data/lib/live_resource/attributes.rb +77 -0
  11. data/lib/live_resource/declarations.rb +200 -0
  12. data/lib/live_resource/finders.rb +43 -0
  13. data/lib/live_resource/log_helper.rb +24 -0
  14. data/lib/live_resource/methods.rb +41 -0
  15. data/lib/live_resource/methods/dispatcher.rb +176 -0
  16. data/lib/live_resource/methods/forward.rb +23 -0
  17. data/lib/live_resource/methods/future.rb +27 -0
  18. data/lib/live_resource/methods/method.rb +93 -0
  19. data/lib/live_resource/methods/token.rb +22 -0
  20. data/lib/live_resource/redis_client.rb +100 -0
  21. data/lib/live_resource/redis_client/attributes.rb +40 -0
  22. data/lib/live_resource/redis_client/methods.rb +194 -0
  23. data/lib/live_resource/redis_client/registration.rb +25 -0
  24. data/lib/live_resource/resource.rb +44 -0
  25. data/lib/live_resource/resource_proxy.rb +180 -0
  26. data/old/benchmark/attribute_benchmark.rb +58 -0
  27. data/old/benchmark/thread_benchmark.rb +89 -0
  28. data/old/examples/attribute.rb +22 -0
  29. data/old/examples/attribute_rmw.rb +30 -0
  30. data/old/examples/attribute_subscriber.rb +32 -0
  31. data/old/examples/method_provider_sleep.rb +22 -0
  32. data/old/examples/methods.rb +37 -0
  33. data/old/lib/live_resource/subscriber.rb +98 -0
  34. data/old/redis_test.rb +127 -0
  35. data/old/state_publisher_test.rb +139 -0
  36. data/old/test/attribute_modify_test.rb +52 -0
  37. data/old/test/attribute_options_test.rb +54 -0
  38. data/old/test/attribute_subscriber_test.rb +94 -0
  39. data/old/test/composite_resource_test.rb +61 -0
  40. data/old/test/method_sender_test.rb +41 -0
  41. data/old/test/redis_api_test.rb +185 -0
  42. data/old/test/simple_attribute_test.rb +75 -0
  43. data/test/attribute_test.rb +212 -0
  44. data/test/declarations_test.rb +119 -0
  45. data/test/logger_test.rb +44 -0
  46. data/test/method_call_test.rb +223 -0
  47. data/test/method_forward_continue_test.rb +83 -0
  48. data/test/method_params_test.rb +81 -0
  49. data/test/method_routing_test.rb +59 -0
  50. data/test/multiple_class_test.rb +47 -0
  51. data/test/new_api_DISABLED.rb +127 -0
  52. data/test/test_helper.rb +9 -0
  53. data/test/volume_create_DISABLED.rb +74 -0
  54. metadata +129 -0
@@ -0,0 +1,185 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ # These tests verify assumptions about the Redis APIs.
4
+ class RedisApiTest < Test::Unit::TestCase
5
+ def setup
6
+ Redis.new.flushall
7
+ @trace = false
8
+ end
9
+
10
+ def trace(s)
11
+ puts("- #{s}") if @trace
12
+ end
13
+
14
+ def publisher(channel, quantity)
15
+ Thread.new do
16
+ trace "Publisher started"
17
+ redis = Redis.new
18
+
19
+ quantity.times do |i|
20
+ trace "Publishing..."
21
+ redis.publish(channel, "news #{i + 1}")
22
+ end
23
+
24
+ trace "Publisher done"
25
+ end
26
+ end
27
+
28
+ def subscriber(channel, quantity, messages)
29
+ started = false
30
+
31
+ thread = Thread.new do
32
+ redis = Redis.new
33
+
34
+ redis.subscribe(channel) do |on|
35
+ on.subscribe do |channel, subscriptions|
36
+ trace "Subscribed to ##{channel} (#{subscriptions} subscriptions)"
37
+ end
38
+
39
+ on.message do |channel, message|
40
+ trace "##{channel}: #{message}"
41
+ messages << message
42
+
43
+ redis.unsubscribe if (messages.length == quantity)
44
+ end
45
+
46
+ on.unsubscribe do |channel, subscriptions|
47
+ trace "Unsubscribed from ##{channel} (#{subscriptions} subscriptions)"
48
+ end
49
+
50
+ trace "Subscriber started"
51
+ started = true
52
+ end
53
+
54
+ trace "Subscriber done"
55
+ end
56
+
57
+ Thread.pass while !started
58
+
59
+ thread
60
+ end
61
+
62
+ def test_publish_subscribe
63
+ messages = Queue.new
64
+
65
+ subscriber = subscriber('test', 1, messages)
66
+ publisher = publisher('test', 1)
67
+
68
+ publisher.join
69
+ subscriber.join
70
+
71
+ assert_equal 'news 1', messages.pop
72
+ end
73
+
74
+ def consume(list, quantity, values)
75
+ Thread.new do
76
+ trace "Consumer started"
77
+ redis = Redis.new
78
+
79
+ quantity.times do
80
+ trace "Consuming..."
81
+ list, value = redis.blpop list, 10
82
+ trace "Consumed: #{value}"
83
+ values << value
84
+ end
85
+
86
+ trace "Consumer done"
87
+ end
88
+ end
89
+
90
+ def produce(list, quantity, delay = 0.0)
91
+ Thread.new do
92
+ trace "Producer started"
93
+ redis = Redis.new
94
+
95
+ quantity.times do
96
+ sleep(delay) unless (delay == 0.0)
97
+
98
+ trace "Producing..."
99
+ redis.rpush list, "hello"
100
+ trace "Produced"
101
+ end
102
+
103
+ trace "Producer done"
104
+ end
105
+ end
106
+
107
+ def test_blocking_pop_consumer_starts_first
108
+ values = Queue.new
109
+
110
+ consumer = consume('test', 1, values)
111
+ sleep 0.1
112
+ producer = produce('test', 1)
113
+
114
+ consumer.join
115
+ producer.join
116
+
117
+ assert_equal "hello", values.pop
118
+ end
119
+
120
+ def test_blocking_pop_producer_starts_first
121
+ values = Queue.new
122
+
123
+ producer = produce('test', 1)
124
+ sleep 0.1
125
+ consumer = consume('test', 1, values)
126
+
127
+ consumer.join
128
+ producer.join
129
+
130
+ assert_equal "hello", values.pop
131
+ end
132
+
133
+ def test_blocking_pop_producer_starts_first
134
+ values = Queue.new
135
+
136
+ producer = produce('test', 1)
137
+ sleep 0.1
138
+ consumer = consume('test', 1, values)
139
+
140
+ consumer.join
141
+ producer.join
142
+
143
+ assert_equal "hello", values.pop
144
+ end
145
+
146
+ def test_blocking_pop_stress
147
+ values = Queue.new
148
+
149
+ producer = produce('test', 1000)
150
+ consumer = consume('test', 1000, values)
151
+
152
+ consumer.join
153
+ producer.join
154
+
155
+ assert_equal "hello", values.pop
156
+ end
157
+
158
+ def test_optimistic_locking
159
+ redis = Redis.new
160
+ redis['key'] = 1;
161
+
162
+ # Test successful case
163
+ redis.watch 'key'
164
+ val = redis['key'].to_i;
165
+ val = val + 1;
166
+ redis.multi
167
+ redis['key'] = val;
168
+ exec_return = redis.exec
169
+ trace "exec (1): #{exec_return.inspect}"
170
+ assert_not_nil exec_return
171
+
172
+ # Test failure case
173
+ redis.watch 'key'
174
+ val = redis['key'].to_i;
175
+ val = val + 1;
176
+
177
+ redis['key'] = 10; # someone else hops in and modifies 'key'
178
+
179
+ redis.multi
180
+ redis['key'] = val;
181
+ exec_return = redis.exec
182
+ trace "exec (2): #{exec_return.inspect}"
183
+ assert_nil exec_return
184
+ end
185
+ end
@@ -0,0 +1,75 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class FavoriteColorServer
4
+ include LiveResource::Attribute
5
+
6
+ remote_writer :color
7
+ remote_accessor :foo, :bar
8
+
9
+ def initialize
10
+ self.namespace = "colors.favorite"
11
+ end
12
+
13
+ def start
14
+ ready = Queue.new
15
+
16
+ thread = Thread.new do
17
+ self.color = 'blue'
18
+ ready << true
19
+ sleep 0.1
20
+ self.color = 'green'
21
+ end
22
+
23
+ ready.pop
24
+ thread
25
+ end
26
+ end
27
+
28
+ class FavoriteColorClient
29
+ include LiveResource::Attribute
30
+
31
+ remote_reader :color
32
+
33
+ def initialize
34
+ self.namespace = "colors.favorite"
35
+ end
36
+ end
37
+
38
+ class SimpleResourceTest < Test::Unit::TestCase
39
+ def setup
40
+ Redis.new.flushall
41
+
42
+ @client = FavoriteColorClient.new
43
+ @server = FavoriteColorServer.new
44
+ end
45
+
46
+ def test_accessors_defined
47
+ assert @client.respond_to? :color
48
+ assert @server.respond_to? :color=
49
+ end
50
+
51
+ def test_simple_read_write
52
+ assert_equal nil, @client.color
53
+
54
+ thread = @server.start
55
+
56
+ assert_equal 'blue', @client.color
57
+ sleep 0.2
58
+ assert_equal 'green', @client.color
59
+
60
+ thread.join
61
+ end
62
+
63
+ def test_accessor
64
+ assert @server.respond_to? :foo
65
+ assert @server.respond_to? :foo=
66
+ assert @server.respond_to? :bar
67
+ assert @server.respond_to? :bar=
68
+
69
+ @server.foo = 'foo'
70
+ assert_equal 'foo', @server.foo
71
+
72
+ @server.bar = @server.foo
73
+ assert_equal 'foo', @server.bar
74
+ end
75
+ end
@@ -0,0 +1,212 @@
1
+ require_relative 'test_helper'
2
+
3
+ class AttributeTest < Test::Unit::TestCase
4
+ class MyClass
5
+ attr_accessor :a, :b
6
+
7
+ def initialize(a, b)
8
+ @a = a
9
+ @b = b
10
+ end
11
+ end
12
+
13
+ class AttributeProvider
14
+ include LiveResource::Resource
15
+
16
+ resource_class :attribute_provider
17
+ resource_name :object_id
18
+
19
+ remote_accessor :string, :integer, :float, :my_class, :nil
20
+ remote_reader :foo
21
+
22
+ def initialize
23
+ self.string = "string"
24
+ self.integer = 42
25
+ self.float = 3.14
26
+ self.my_class = MyClass.new("foo", 42)
27
+ self.nil = nil
28
+ end
29
+
30
+ def set_foo(value, overwrite=true)
31
+ if overwrite
32
+ remote_attribute_write(:foo, value)
33
+ else
34
+ remote_attribute_writenx(:foo, value)
35
+ end
36
+ end
37
+ end
38
+
39
+ def setup
40
+ Redis.new.flushall
41
+
42
+ LiveResource::RedisClient.logger.level = Logger::INFO
43
+
44
+ AttributeProvider.new
45
+ end
46
+
47
+ def teardown
48
+ LiveResource::stop
49
+ end
50
+
51
+ def test_string_attribute
52
+ ap = LiveResource::any(:attribute_provider)
53
+
54
+ assert ap.string.kind_of?(String), "String attribute is not a string"
55
+ assert_equal "string", ap.string
56
+
57
+ ap.string = "new string"
58
+ assert_equal "new string", ap.string
59
+ end
60
+
61
+ def test_numeric_attributes
62
+ ap = LiveResource::any(:attribute_provider)
63
+
64
+ assert ap.integer.kind_of?(Numeric), "Integer attribute is not a numeric"
65
+ assert ap.float.kind_of?(Numeric), "Float attribute is not a numeric"
66
+ assert ap.float.kind_of?(Float), "Float attribute is not a float"
67
+
68
+ assert_equal 42, ap.integer
69
+ assert_equal 3.14, ap.float
70
+
71
+ ap.integer = 24
72
+ assert_equal 24, ap.integer
73
+ end
74
+
75
+ def test_custom_attribute
76
+ ap = LiveResource::any(:attribute_provider)
77
+ mc = ap.my_class
78
+
79
+ assert mc.kind_of?(MyClass), "MyClass attribute is not a MyClass"
80
+
81
+ assert_equal "foo", mc.a
82
+ assert_equal 42, mc.b
83
+ end
84
+
85
+ def test_nil_attribute
86
+ ap = LiveResource::any(:attribute_provider)
87
+
88
+ assert_equal nil, ap.nil
89
+ end
90
+
91
+ def test_no_overwrite
92
+ ap = LiveResource::any(:attribute_provider)
93
+
94
+ # Foo should be not set.
95
+ assert_equal nil, ap.foo
96
+
97
+ # Set it for the first time, telling it not overwrite. This should work, as it
98
+ # has never been set before
99
+ ap.set_foo(23, false)
100
+ assert_equal 23, ap.foo
101
+
102
+ # Try again, it should not be overwritten
103
+ ap.set_foo(13, false)
104
+ assert_equal 23, ap.foo
105
+
106
+ # Now overwrite it
107
+ ap.set_foo(13, true)
108
+ assert_equal 13, ap.foo
109
+ end
110
+ end
111
+
112
+ class AttributeModifyTest < Test::Unit::TestCase
113
+ class Incrementer
114
+ include LiveResource::Resource
115
+
116
+ resource_class :incrementer
117
+ resource_name :object_id
118
+
119
+ remote_accessor :value1, :value2
120
+
121
+ def initialize(value1, value2)
122
+ self.value1 = value1
123
+ self.value2 = value2
124
+ end
125
+
126
+ def increment(*values)
127
+ remote_attribute_modify(*values) do |a, v|
128
+ v + 1
129
+ end
130
+ end
131
+
132
+ def increment_with_interference
133
+ modified = false
134
+ remote_attribute_modify(:value1, :value2) do |a, v|
135
+ # modify the value from a different redis the
136
+ # first time through
137
+ unless modified
138
+ self.redis.clone.attribute_write(a, 10, {})
139
+ modified = true
140
+ end
141
+ v + 1
142
+ end
143
+ end
144
+ end
145
+
146
+ def setup
147
+ Redis.new.flushall
148
+
149
+ LiveResource::RedisClient.logger.level = Logger::INFO
150
+
151
+ Incrementer.new(1, 1)
152
+ end
153
+
154
+ def teardown
155
+ LiveResource::stop
156
+ end
157
+
158
+ def test_modify_without_interference
159
+ i = LiveResource::any(:incrementer)
160
+
161
+ assert_equal 1, i.value1
162
+
163
+ i.increment(:value1)
164
+
165
+ assert_equal 2, i.value1
166
+ end
167
+
168
+ def test_multi_modify_without_interference
169
+ i = LiveResource::any(:incrementer)
170
+
171
+ assert_equal 1, i.value1
172
+ assert_equal 1, i.value2
173
+
174
+ i.increment(:value1, :value2)
175
+
176
+ assert_equal 2, i.value1
177
+ assert_equal 2, i.value2
178
+ end
179
+
180
+ def test_modify_with_interference
181
+ i = LiveResource::any(:incrementer)
182
+
183
+ assert_equal 1, i.value1
184
+ assert_equal 1, i.value2
185
+
186
+ i.increment_with_interference
187
+
188
+ # Because of the interference, we should now be at 10
189
+ # for value1 but still only be at 2 for value2
190
+ assert_equal 11, i.value1
191
+ assert_equal 2, i.value2
192
+ end
193
+
194
+ def test_modify_invalid_attributes
195
+ i = LiveResource::any(:incrementer)
196
+
197
+ # Just a single invalid attr
198
+ assert_raise(ArgumentError) do
199
+ i.increment(:value3)
200
+ end
201
+
202
+ # Now two
203
+ assert_raise(ArgumentError) do
204
+ i.increment(:value3, :value4)
205
+ end
206
+
207
+ # Now mix with a valid attr
208
+ assert_raise(ArgumentError) do
209
+ i.increment(:value1, :value3)
210
+ end
211
+ end
212
+ end