discourse-redis 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +59 -0
  4. data/.travis/Gemfile +11 -0
  5. data/.yardopts +3 -0
  6. data/CHANGELOG.md +349 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +328 -0
  10. data/Rakefile +87 -0
  11. data/benchmarking/logging.rb +71 -0
  12. data/benchmarking/pipeline.rb +51 -0
  13. data/benchmarking/speed.rb +21 -0
  14. data/benchmarking/suite.rb +24 -0
  15. data/benchmarking/worker.rb +71 -0
  16. data/examples/basic.rb +15 -0
  17. data/examples/consistency.rb +114 -0
  18. data/examples/dist_redis.rb +43 -0
  19. data/examples/incr-decr.rb +17 -0
  20. data/examples/list.rb +26 -0
  21. data/examples/pubsub.rb +37 -0
  22. data/examples/sentinel.rb +41 -0
  23. data/examples/sentinel/start +49 -0
  24. data/examples/sets.rb +36 -0
  25. data/examples/unicorn/config.ru +3 -0
  26. data/examples/unicorn/unicorn.rb +20 -0
  27. data/lib/redis.rb +2731 -0
  28. data/lib/redis/client.rb +575 -0
  29. data/lib/redis/connection.rb +9 -0
  30. data/lib/redis/connection/command_helper.rb +44 -0
  31. data/lib/redis/connection/hiredis.rb +64 -0
  32. data/lib/redis/connection/registry.rb +12 -0
  33. data/lib/redis/connection/ruby.rb +322 -0
  34. data/lib/redis/connection/synchrony.rb +124 -0
  35. data/lib/redis/distributed.rb +873 -0
  36. data/lib/redis/errors.rb +40 -0
  37. data/lib/redis/hash_ring.rb +132 -0
  38. data/lib/redis/pipeline.rb +141 -0
  39. data/lib/redis/subscribe.rb +83 -0
  40. data/lib/redis/version.rb +3 -0
  41. data/redis.gemspec +34 -0
  42. data/test/bitpos_test.rb +69 -0
  43. data/test/blocking_commands_test.rb +42 -0
  44. data/test/command_map_test.rb +30 -0
  45. data/test/commands_on_hashes_test.rb +21 -0
  46. data/test/commands_on_hyper_log_log_test.rb +21 -0
  47. data/test/commands_on_lists_test.rb +20 -0
  48. data/test/commands_on_sets_test.rb +77 -0
  49. data/test/commands_on_sorted_sets_test.rb +137 -0
  50. data/test/commands_on_strings_test.rb +101 -0
  51. data/test/commands_on_value_types_test.rb +133 -0
  52. data/test/connection_handling_test.rb +250 -0
  53. data/test/distributed_blocking_commands_test.rb +46 -0
  54. data/test/distributed_commands_on_hashes_test.rb +10 -0
  55. data/test/distributed_commands_on_hyper_log_log_test.rb +33 -0
  56. data/test/distributed_commands_on_lists_test.rb +22 -0
  57. data/test/distributed_commands_on_sets_test.rb +83 -0
  58. data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
  59. data/test/distributed_commands_on_strings_test.rb +59 -0
  60. data/test/distributed_commands_on_value_types_test.rb +95 -0
  61. data/test/distributed_commands_requiring_clustering_test.rb +164 -0
  62. data/test/distributed_connection_handling_test.rb +23 -0
  63. data/test/distributed_internals_test.rb +79 -0
  64. data/test/distributed_key_tags_test.rb +52 -0
  65. data/test/distributed_persistence_control_commands_test.rb +26 -0
  66. data/test/distributed_publish_subscribe_test.rb +92 -0
  67. data/test/distributed_remote_server_control_commands_test.rb +66 -0
  68. data/test/distributed_scripting_test.rb +102 -0
  69. data/test/distributed_sorting_test.rb +20 -0
  70. data/test/distributed_test.rb +58 -0
  71. data/test/distributed_transactions_test.rb +32 -0
  72. data/test/encoding_test.rb +18 -0
  73. data/test/error_replies_test.rb +59 -0
  74. data/test/fork_safety_test.rb +65 -0
  75. data/test/helper.rb +232 -0
  76. data/test/helper_test.rb +24 -0
  77. data/test/internals_test.rb +437 -0
  78. data/test/lint/blocking_commands.rb +150 -0
  79. data/test/lint/hashes.rb +162 -0
  80. data/test/lint/hyper_log_log.rb +60 -0
  81. data/test/lint/lists.rb +143 -0
  82. data/test/lint/sets.rb +125 -0
  83. data/test/lint/sorted_sets.rb +316 -0
  84. data/test/lint/strings.rb +260 -0
  85. data/test/lint/value_types.rb +122 -0
  86. data/test/persistence_control_commands_test.rb +26 -0
  87. data/test/pipelining_commands_test.rb +242 -0
  88. data/test/publish_subscribe_test.rb +254 -0
  89. data/test/remote_server_control_commands_test.rb +118 -0
  90. data/test/scanning_test.rb +413 -0
  91. data/test/scripting_test.rb +78 -0
  92. data/test/sentinel_command_test.rb +80 -0
  93. data/test/sentinel_test.rb +255 -0
  94. data/test/sorting_test.rb +59 -0
  95. data/test/support/connection/hiredis.rb +1 -0
  96. data/test/support/connection/ruby.rb +1 -0
  97. data/test/support/connection/synchrony.rb +17 -0
  98. data/test/support/redis_mock.rb +119 -0
  99. data/test/support/wire/synchrony.rb +24 -0
  100. data/test/support/wire/thread.rb +5 -0
  101. data/test/synchrony_driver.rb +88 -0
  102. data/test/test.conf.erb +9 -0
  103. data/test/thread_safety_test.rb +32 -0
  104. data/test/transactions_test.rb +264 -0
  105. data/test/unknown_commands_test.rb +14 -0
  106. data/test/url_param_test.rb +138 -0
  107. metadata +182 -0
@@ -0,0 +1,79 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestDistributedInternals < Test::Unit::TestCase
6
+
7
+ include Helper::Distributed
8
+
9
+ def test_provides_a_meaningful_inspect
10
+ nodes = ["redis://localhost:#{PORT}/15", *NODES]
11
+ redis = Redis::Distributed.new nodes
12
+
13
+ assert_equal "#<Redis client v#{Redis::VERSION} for #{redis.nodes.map(&:id).join(', ')}>", redis.inspect
14
+ end
15
+
16
+ def test_default_as_urls
17
+ nodes = ["redis://localhost:#{PORT}/15", *NODES]
18
+ redis = Redis::Distributed.new nodes
19
+ assert_equal ["redis://localhost:#{PORT}/15", *NODES], redis.nodes.map { |node| node.client.id}
20
+ end
21
+
22
+ def test_default_as_config_hashes
23
+ nodes = [OPTIONS.merge(:host => '127.0.0.1'), OPTIONS.merge(:host => 'somehost', :port => PORT.next)]
24
+ redis = Redis::Distributed.new nodes
25
+ assert_equal ["redis://127.0.0.1:#{PORT}/15","redis://somehost:#{PORT.next}/15"], redis.nodes.map { |node| node.client.id }
26
+ end
27
+
28
+ def test_as_mix_and_match
29
+ nodes = ["redis://127.0.0.1:7389/15", OPTIONS.merge(:host => 'somehost'), OPTIONS.merge(:host => 'somehost', :port => PORT.next)]
30
+ redis = Redis::Distributed.new nodes
31
+ assert_equal ["redis://127.0.0.1:7389/15", "redis://somehost:#{PORT}/15", "redis://somehost:#{PORT.next}/15"], redis.nodes.map { |node| node.client.id }
32
+ end
33
+
34
+ def test_override_id
35
+ nodes = [OPTIONS.merge(:host => '127.0.0.1', :id => "test"), OPTIONS.merge( :host => 'somehost', :port => PORT.next, :id => "test1")]
36
+ redis = Redis::Distributed.new nodes
37
+ assert_equal redis.nodes.first.client.id, "test"
38
+ assert_equal redis.nodes.last.client.id, "test1"
39
+ assert_equal "#<Redis client v#{Redis::VERSION} for #{redis.nodes.map(&:id).join(', ')}>", redis.inspect
40
+ end
41
+
42
+ def test_can_be_duped_to_create_a_new_connection
43
+ redis = Redis::Distributed.new(NODES)
44
+
45
+ clients = redis.info[0]["connected_clients"].to_i
46
+
47
+ r2 = redis.dup
48
+ r2.ping
49
+
50
+ assert_equal clients + 1, redis.info[0]["connected_clients"].to_i
51
+ end
52
+
53
+ def test_keeps_options_after_dup
54
+ r1 = Redis::Distributed.new(NODES, :tag => /^(\w+):/)
55
+
56
+ assert_raise(Redis::Distributed::CannotDistribute) do
57
+ r1.sinter("foo", "bar")
58
+ end
59
+
60
+ assert_equal [], r1.sinter("baz:foo", "baz:bar")
61
+
62
+ r2 = r1.dup
63
+
64
+ assert_raise(Redis::Distributed::CannotDistribute) do
65
+ r2.sinter("foo", "bar")
66
+ end
67
+
68
+ assert_equal [], r2.sinter("baz:foo", "baz:bar")
69
+ end
70
+
71
+ def test_colliding_node_ids
72
+ nodes = ["redis://localhost:#{PORT}/15", "redis://localhost:#{PORT}/15", *NODES]
73
+
74
+ assert_raise(RuntimeError) do
75
+ redis = Redis::Distributed.new nodes
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestDistributedKeyTags < Test::Unit::TestCase
6
+
7
+ include Helper
8
+ include Helper::Distributed
9
+
10
+ def test_hashes_consistently
11
+ r1 = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
12
+ r2 = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
13
+ r3 = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
14
+
15
+ assert_equal r1.node_for("foo").id, r2.node_for("foo").id
16
+ assert_equal r1.node_for("foo").id, r3.node_for("foo").id
17
+ end
18
+
19
+ def test_allows_clustering_of_keys
20
+ r = Redis::Distributed.new(NODES)
21
+ r.add_node("redis://127.0.0.1:#{PORT}/14")
22
+ r.flushdb
23
+
24
+ 100.times do |i|
25
+ r.set "{foo}users:#{i}", i
26
+ end
27
+
28
+ assert_equal [0, 100], r.nodes.map { |node| node.keys.size }
29
+ end
30
+
31
+ def test_distributes_keys_if_no_clustering_is_used
32
+ r.add_node("redis://127.0.0.1:#{PORT}/14")
33
+ r.flushdb
34
+
35
+ r.set "users:1", 1
36
+ r.set "users:4", 4
37
+
38
+ assert_equal [1, 1], r.nodes.map { |node| node.keys.size }
39
+ end
40
+
41
+ def test_allows_passing_a_custom_tag_extractor
42
+ r = Redis::Distributed.new(NODES, :tag => /^(.+?):/)
43
+ r.add_node("redis://127.0.0.1:#{PORT}/14")
44
+ r.flushdb
45
+
46
+ 100.times do |i|
47
+ r.set "foo:users:#{i}", i
48
+ end
49
+
50
+ assert_equal [0, 100], r.nodes.map { |node| node.keys.size }
51
+ end
52
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestDistributedPersistenceControlCommands < Test::Unit::TestCase
6
+
7
+ include Helper::Distributed
8
+
9
+ def test_save
10
+ redis_mock(:save => lambda { "+SAVE" }) do |redis|
11
+ assert_equal ["SAVE"], redis.save
12
+ end
13
+ end
14
+
15
+ def test_bgsave
16
+ redis_mock(:bgsave => lambda { "+BGSAVE" }) do |redis|
17
+ assert_equal ["BGSAVE"], redis.bgsave
18
+ end
19
+ end
20
+
21
+ def test_lastsave
22
+ redis_mock(:lastsave => lambda { "+LASTSAVE" }) do |redis|
23
+ assert_equal ["LASTSAVE"], redis.lastsave
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,92 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestDistributedPublishSubscribe < Test::Unit::TestCase
6
+
7
+ include Helper::Distributed
8
+
9
+ def test_subscribe_and_unsubscribe
10
+ assert_raise Redis::Distributed::CannotDistribute do
11
+ r.subscribe("foo", "bar") { }
12
+ end
13
+
14
+ assert_raise Redis::Distributed::CannotDistribute do
15
+ r.subscribe("{qux}foo", "bar") { }
16
+ end
17
+ end
18
+
19
+ def test_subscribe_and_unsubscribe_with_tags
20
+ @subscribed = false
21
+ @unsubscribed = false
22
+
23
+ wire = Wire.new do
24
+ r.subscribe("foo") do |on|
25
+ on.subscribe do |channel, total|
26
+ @subscribed = true
27
+ @t1 = total
28
+ end
29
+
30
+ on.message do |channel, message|
31
+ if message == "s1"
32
+ r.unsubscribe
33
+ @message = message
34
+ end
35
+ end
36
+
37
+ on.unsubscribe do |channel, total|
38
+ @unsubscribed = true
39
+ @t2 = total
40
+ end
41
+ end
42
+ end
43
+
44
+ # Wait until the subscription is active before publishing
45
+ Wire.pass while !@subscribed
46
+
47
+ Redis::Distributed.new(NODES).publish("foo", "s1")
48
+
49
+ wire.join
50
+
51
+ assert @subscribed
52
+ assert_equal 1, @t1
53
+ assert @unsubscribed
54
+ assert_equal 0, @t2
55
+ assert_equal "s1", @message
56
+ end
57
+
58
+ def test_subscribe_within_subscribe
59
+ @channels = []
60
+
61
+ wire = Wire.new do
62
+ r.subscribe("foo") do |on|
63
+ on.subscribe do |channel, total|
64
+ @channels << channel
65
+
66
+ r.subscribe("bar") if channel == "foo"
67
+ r.unsubscribe if channel == "bar"
68
+ end
69
+ end
70
+ end
71
+
72
+ wire.join
73
+
74
+ assert_equal ["foo", "bar"], @channels
75
+ end
76
+
77
+ def test_other_commands_within_a_subscribe
78
+ assert_raise Redis::CommandError do
79
+ r.subscribe("foo") do |on|
80
+ on.subscribe do |channel, total|
81
+ r.set("bar", "s2")
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def test_subscribe_without_a_block
88
+ assert_raise LocalJumpError do
89
+ r.subscribe("foo")
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestDistributedRemoteServerControlCommands < Test::Unit::TestCase
6
+
7
+ include Helper::Distributed
8
+
9
+ def test_info
10
+ keys = [
11
+ "redis_version",
12
+ "uptime_in_seconds",
13
+ "uptime_in_days",
14
+ "connected_clients",
15
+ "used_memory",
16
+ "total_connections_received",
17
+ "total_commands_processed",
18
+ ]
19
+
20
+ infos = r.info
21
+
22
+ infos.each do |info|
23
+ keys.each do |k|
24
+ msg = "expected #info to include #{k}"
25
+ assert info.keys.include?(k), msg
26
+ end
27
+ end
28
+ end
29
+
30
+ def test_info_commandstats
31
+ target_version "2.5.7" do
32
+ r.nodes.each { |n| n.config(:resetstat) }
33
+ r.ping # Executed on every node
34
+
35
+ r.info(:commandstats).each do |info|
36
+ assert_equal "1", info["ping"]["calls"]
37
+ end
38
+ end
39
+ end
40
+
41
+ def test_monitor
42
+ begin
43
+ r.monitor
44
+ rescue Exception => ex
45
+ ensure
46
+ assert ex.kind_of?(NotImplementedError)
47
+ end
48
+ end
49
+
50
+ def test_echo
51
+ assert_equal ["foo bar baz\n"], r.echo("foo bar baz\n")
52
+ end
53
+
54
+ def test_time
55
+ target_version "2.5.4" do
56
+ # Test that the difference between the time that Ruby reports and the time
57
+ # that Redis reports is minimal (prevents the test from being racy).
58
+ r.time.each do |rv|
59
+ redis_usec = rv[0] * 1_000_000 + rv[1]
60
+ ruby_usec = Integer(Time.now.to_f * 1_000_000)
61
+
62
+ assert 500_000 > (ruby_usec - redis_usec).abs
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,102 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestDistributedScripting < Test::Unit::TestCase
6
+
7
+ include Helper::Distributed
8
+
9
+ def to_sha(script)
10
+ r.script(:load, script).first
11
+ end
12
+
13
+ def test_script_exists
14
+ target_version "2.5.9" do # 2.6-rc1
15
+ a = to_sha("return 1")
16
+ b = a.succ
17
+
18
+ assert_equal [true], r.script(:exists, a)
19
+ assert_equal [false], r.script(:exists, b)
20
+ assert_equal [[true]], r.script(:exists, [a])
21
+ assert_equal [[false]], r.script(:exists, [b])
22
+ assert_equal [[true, false]], r.script(:exists, [a, b])
23
+ end
24
+ end
25
+
26
+ def test_script_flush
27
+ target_version "2.5.9" do # 2.6-rc1
28
+ sha = to_sha("return 1")
29
+ assert r.script(:exists, sha).first
30
+ assert_equal ["OK"], r.script(:flush)
31
+ assert !r.script(:exists, sha).first
32
+ end
33
+ end
34
+
35
+ def test_script_kill
36
+ target_version "2.5.9" do # 2.6-rc1
37
+ redis_mock(:script => lambda { |arg| "+#{arg.upcase}" }) do |redis|
38
+ assert_equal ["KILL"], redis.script(:kill)
39
+ end
40
+ end
41
+ end
42
+
43
+ def test_eval
44
+ target_version "2.5.9" do # 2.6-rc1
45
+ assert_raises(Redis::Distributed::CannotDistribute) do
46
+ r.eval("return #KEYS")
47
+ end
48
+
49
+ assert_raises(Redis::Distributed::CannotDistribute) do
50
+ r.eval("return KEYS", ["k1", "k2"])
51
+ end
52
+
53
+ assert_equal ["k1"], r.eval("return KEYS", ["k1"])
54
+ assert_equal ["a1", "a2"], r.eval("return ARGV", ["k1"], ["a1", "a2"])
55
+ end
56
+ end
57
+
58
+ def test_eval_with_options_hash
59
+ target_version "2.5.9" do # 2.6-rc1
60
+ assert_raises(Redis::Distributed::CannotDistribute) do
61
+ r.eval("return #KEYS", {})
62
+ end
63
+
64
+ assert_raises(Redis::Distributed::CannotDistribute) do
65
+ r.eval("return KEYS", { :keys => ["k1", "k2"] })
66
+ end
67
+
68
+ assert_equal ["k1"], r.eval("return KEYS", { :keys => ["k1"] })
69
+ assert_equal ["a1", "a2"], r.eval("return ARGV", { :keys => ["k1"], :argv => ["a1", "a2"] })
70
+ end
71
+ end
72
+
73
+ def test_evalsha
74
+ target_version "2.5.9" do # 2.6-rc1
75
+ assert_raises(Redis::Distributed::CannotDistribute) do
76
+ r.evalsha(to_sha("return #KEYS"))
77
+ end
78
+
79
+ assert_raises(Redis::Distributed::CannotDistribute) do
80
+ r.evalsha(to_sha("return KEYS"), ["k1", "k2"])
81
+ end
82
+
83
+ assert_equal ["k1"], r.evalsha(to_sha("return KEYS"), ["k1"])
84
+ assert_equal ["a1", "a2"], r.evalsha(to_sha("return ARGV"), ["k1"], ["a1", "a2"])
85
+ end
86
+ end
87
+
88
+ def test_evalsha_with_options_hash
89
+ target_version "2.5.9" do # 2.6-rc1
90
+ assert_raises(Redis::Distributed::CannotDistribute) do
91
+ r.evalsha(to_sha("return #KEYS"), {})
92
+ end
93
+
94
+ assert_raises(Redis::Distributed::CannotDistribute) do
95
+ r.evalsha(to_sha("return KEYS"), { :keys => ["k1", "k2"] })
96
+ end
97
+
98
+ assert_equal ["k1"], r.evalsha(to_sha("return KEYS"), { :keys => ["k1"] })
99
+ assert_equal ["a1", "a2"], r.evalsha(to_sha("return ARGV"), { :keys => ["k1"], :argv => ["a1", "a2"] })
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestDistributedSorting < Test::Unit::TestCase
6
+
7
+ include Helper::Distributed
8
+
9
+ def test_sort
10
+ assert_raise(Redis::Distributed::CannotDistribute) do
11
+ r.set("foo:1", "s1")
12
+ r.set("foo:2", "s2")
13
+
14
+ r.rpush("bar", "1")
15
+ r.rpush("bar", "2")
16
+
17
+ r.sort("bar", :get => "foo:*", :limit => [0, 1])
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestDistributed < Test::Unit::TestCase
6
+
7
+ include Helper::Distributed
8
+
9
+ def test_handle_multiple_servers
10
+ @r = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
11
+
12
+ 100.times do |idx|
13
+ @r.set(idx.to_s, "foo#{idx}")
14
+ end
15
+
16
+ 100.times do |idx|
17
+ assert_equal "foo#{idx}", @r.get(idx.to_s)
18
+ end
19
+
20
+ assert_equal "0", @r.keys("*").sort.first
21
+ assert_equal "string", @r.type("1")
22
+ end
23
+
24
+ def test_add_nodes
25
+ logger = Logger.new("/dev/null")
26
+
27
+ @r = Redis::Distributed.new NODES, :logger => logger, :timeout => 10
28
+
29
+ assert_equal "127.0.0.1", @r.nodes[0].client.host
30
+ assert_equal PORT, @r.nodes[0].client.port
31
+ assert_equal 15, @r.nodes[0].client.db
32
+ assert_equal 10, @r.nodes[0].client.timeout
33
+ assert_equal logger, @r.nodes[0].client.logger
34
+
35
+ @r.add_node("redis://127.0.0.1:6380/14")
36
+
37
+ assert_equal "127.0.0.1", @r.nodes[1].client.host
38
+ assert_equal 6380, @r.nodes[1].client.port
39
+ assert_equal 14, @r.nodes[1].client.db
40
+ assert_equal 10, @r.nodes[1].client.timeout
41
+ assert_equal logger, @r.nodes[1].client.logger
42
+ end
43
+
44
+ def test_pipelining_commands_cannot_be_distributed
45
+ assert_raise Redis::Distributed::CannotDistribute do
46
+ r.pipelined do
47
+ r.lpush "foo", "s1"
48
+ r.lpush "foo", "s2"
49
+ end
50
+ end
51
+ end
52
+
53
+ def test_unknown_commands_does_not_work_by_default
54
+ assert_raise NoMethodError do
55
+ r.not_yet_implemented_command
56
+ end
57
+ end
58
+ end