liveresource 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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