mongo 0.18.3 → 0.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/README.rdoc +41 -50
  2. data/Rakefile +14 -4
  3. data/examples/gridfs.rb +25 -70
  4. data/lib/mongo.rb +4 -2
  5. data/lib/mongo/collection.rb +70 -89
  6. data/lib/mongo/connection.rb +203 -43
  7. data/lib/mongo/cursor.rb +7 -7
  8. data/lib/mongo/db.rb +61 -18
  9. data/lib/mongo/exceptions.rb +7 -1
  10. data/lib/mongo/gridfs.rb +8 -1
  11. data/lib/mongo/gridfs/chunk.rb +2 -1
  12. data/lib/mongo/gridfs/grid.rb +90 -0
  13. data/lib/mongo/gridfs/grid_file_system.rb +113 -0
  14. data/lib/mongo/gridfs/grid_io.rb +339 -0
  15. data/lib/mongo/gridfs/grid_store.rb +43 -18
  16. data/lib/mongo/types/binary.rb +5 -1
  17. data/lib/mongo/types/code.rb +1 -1
  18. data/lib/mongo/types/dbref.rb +3 -1
  19. data/lib/mongo/types/min_max_keys.rb +1 -1
  20. data/lib/mongo/types/objectid.rb +16 -55
  21. data/lib/mongo/types/regexp_of_holding.rb +1 -1
  22. data/lib/mongo/util/bson_c.rb +2 -2
  23. data/lib/mongo/util/bson_ruby.rb +22 -11
  24. data/lib/mongo/util/byte_buffer.rb +1 -1
  25. data/lib/mongo/util/conversions.rb +1 -1
  26. data/lib/mongo/util/ordered_hash.rb +6 -1
  27. data/lib/mongo/util/server_version.rb +1 -1
  28. data/lib/mongo/util/support.rb +1 -1
  29. data/mongo-ruby-driver.gemspec +1 -1
  30. data/test/auxillary/authentication_test.rb +68 -0
  31. data/test/auxillary/autoreconnect_test.rb +41 -0
  32. data/test/binary_test.rb +15 -0
  33. data/test/{test_bson.rb → bson_test.rb} +63 -6
  34. data/test/{test_byte_buffer.rb → byte_buffer_test.rb} +0 -0
  35. data/test/{test_chunk.rb → chunk_test.rb} +0 -0
  36. data/test/{test_collection.rb → collection_test.rb} +35 -39
  37. data/test/{test_connection.rb → connection_test.rb} +33 -3
  38. data/test/{test_conversions.rb → conversions_test.rb} +0 -0
  39. data/test/{test_cursor.rb → cursor_test.rb} +0 -7
  40. data/test/{test_db_api.rb → db_api_test.rb} +3 -6
  41. data/test/{test_db_connection.rb → db_connection_test.rb} +0 -0
  42. data/test/{test_db.rb → db_test.rb} +33 -15
  43. data/test/grid_file_system_test.rb +210 -0
  44. data/test/grid_io_test.rb +78 -0
  45. data/test/{test_grid_store.rb → grid_store_test.rb} +33 -2
  46. data/test/grid_test.rb +87 -0
  47. data/test/{test_objectid.rb → objectid_test.rb} +2 -33
  48. data/test/{test_ordered_hash.rb → ordered_hash_test.rb} +4 -0
  49. data/test/{test_slave_connection.rb → slave_connection_test.rb} +0 -0
  50. data/test/test_helper.rb +2 -2
  51. data/test/{test_threading.rb → threading_test.rb} +0 -0
  52. data/test/unit/collection_test.rb +12 -3
  53. data/test/unit/connection_test.rb +85 -24
  54. data/test/unit/cursor_test.rb +1 -2
  55. data/test/unit/db_test.rb +70 -69
  56. metadata +27 -23
  57. data/bin/objectid_benchmark.rb +0 -23
  58. data/bin/perf.rb +0 -30
  59. data/lib/mongo/admin.rb +0 -95
  60. data/lib/mongo/util/xml_to_ruby.rb +0 -112
  61. data/test/test_admin.rb +0 -67
  62. data/test/test_round_trip.rb +0 -114
@@ -48,13 +48,8 @@ class ObjectIDTest < Test::Unit::TestCase
48
48
  assert_equal 24, $1.length
49
49
  end
50
50
 
51
- def test_to_s_legacy
52
- s = @o.to_s_legacy
53
- assert_equal 24, s.length
54
- s =~ /^([0-9a-f]+)$/
55
- assert_equal 24, $1.length
56
-
57
- assert_not_equal s, @o.to_s
51
+ def test_inspect
52
+ assert_equal "ObjectID('#{@o.to_s}')", @o.inspect
58
53
  end
59
54
 
60
55
  def test_save_and_restore
@@ -85,20 +80,6 @@ class ObjectIDTest < Test::Unit::TestCase
85
80
  end
86
81
  end
87
82
 
88
- def test_from_string_legacy
89
- hex_str = @o.to_s_legacy
90
- o2 = ObjectID.from_string_legacy(hex_str)
91
- assert_equal hex_str, o2.to_s_legacy
92
- assert_equal @o, o2
93
- assert_equal @o.to_s, o2.to_s
94
- end
95
-
96
- def test_illegal_from_string_legacy
97
- assert_raise InvalidObjectID do
98
- ObjectID.from_string_legacy("")
99
- end
100
- end
101
-
102
83
  def test_legal
103
84
  assert !ObjectID.legal?(nil)
104
85
  assert !ObjectID.legal?("fred")
@@ -121,18 +102,6 @@ class ObjectIDTest < Test::Unit::TestCase
121
102
  assert_equal [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b], o.to_a
122
103
  end
123
104
 
124
- def test_legacy_byte_order
125
- hex_str = '000102030405060708090A0B'
126
- o = ObjectID.from_string_legacy(hex_str)
127
- assert_equal [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08], o.to_a
128
- end
129
-
130
- def test_legacy_string_convert
131
- l = @o.to_s_legacy
132
- s = @o.to_s
133
- assert_equal s, ObjectID.legacy_string_convert(l)
134
- end
135
-
136
105
  def test_generation_time
137
106
  time = Time.now
138
107
  id = ObjectID.new
@@ -76,6 +76,10 @@ class OrderedHashTest < Test::Unit::TestCase
76
76
  assert_equal @ordered_keys, @oh.keys
77
77
  end
78
78
 
79
+ def test_to_a_order_preserved
80
+ assert_equal @ordered_keys, @oh.to_a.map {|m| m.first}
81
+ end
82
+
79
83
  def test_order_preserved_after_replace
80
84
  @oh['a'] = 42
81
85
  assert_equal @ordered_keys, @oh.keys
data/test/test_helper.rb CHANGED
@@ -10,8 +10,8 @@ begin
10
10
  rescue LoadError
11
11
  puts <<MSG
12
12
 
13
- This test suite now requires shoulda and mocha.
14
- You can install these gems as follows:
13
+ This test suite requires shoulda and mocha.
14
+ You can install them as follows:
15
15
  gem install shoulda
16
16
  gem install mocha
17
17
 
File without changes
@@ -1,6 +1,6 @@
1
1
  require 'test/test_helper'
2
2
 
3
- class ConnectionTest < Test::Unit::TestCase
3
+ class CollectionTest < Test::Unit::TestCase
4
4
 
5
5
  context "Basic operations: " do
6
6
  setup do
@@ -27,6 +27,17 @@ class ConnectionTest < Test::Unit::TestCase
27
27
  @coll.insert({:title => 'Moby Dick'})
28
28
  end
29
29
 
30
+ should "not log binary data" do
31
+ @conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
32
+ @db = @conn['testing']
33
+ @coll = @db.collection('books')
34
+ data = Mongo::Binary.new(("BINARY " * 1000).unpack("c*"))
35
+ @conn.expects(:send_message).with do |op, msg, log|
36
+ op == 2002 && log.include?("Mongo::Binary")
37
+ end
38
+ @coll.insert({:data => data})
39
+ end
40
+
30
41
  should "send safe update message" do
31
42
  @conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
32
43
  @db = @conn['testing']
@@ -48,5 +59,3 @@ class ConnectionTest < Test::Unit::TestCase
48
59
  end
49
60
  end
50
61
  end
51
-
52
-
@@ -1,18 +1,19 @@
1
1
  require 'test/test_helper'
2
+ include Mongo
2
3
 
3
4
  class ConnectionTest < Test::Unit::TestCase
4
-
5
- def new_mock_socket
6
- socket = Object.new
7
- socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
8
- socket
9
- end
10
-
11
- def new_mock_db
12
- db = Object.new
13
- end
14
-
15
5
  context "Initialization: " do
6
+ setup do
7
+ def new_mock_socket
8
+ socket = Object.new
9
+ socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
10
+ socket
11
+ end
12
+
13
+ def new_mock_db
14
+ db = Object.new
15
+ end
16
+ end
16
17
 
17
18
  context "given a single node" do
18
19
  setup do
@@ -38,22 +39,82 @@ class ConnectionTest < Test::Unit::TestCase
38
39
  assert !@conn.slave_ok?
39
40
  end
40
41
  end
41
- end
42
42
 
43
- context "with a nonstandard port" do
44
- setup do
45
- TCPSocket.stubs(:new).returns(new_mock_socket)
46
- @conn = Connection.new('255.255.255.255', 2500, :connect => false)
47
- admin_db = new_mock_db
48
- admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
49
- @conn.expects(:[]).with('admin').returns(admin_db)
50
- @conn.connect_to_master
43
+ context "initializing a paired connection" do
44
+ should "require left and right nodes" do
45
+ assert_raise MongoArgumentError do
46
+ Connection.paired(['localhost', 27018], :connect => false)
47
+ end
48
+
49
+ assert_raise MongoArgumentError do
50
+ Connection.paired(['localhost', 27018], :connect => false)
51
+ end
52
+ end
53
+
54
+ should "store both nodes" do
55
+ @conn = Connection.paired([['localhost', 27017], ['localhost', 27018]], :connect => false)
56
+
57
+ assert_equal ['localhost', 27017], @conn.nodes[0]
58
+ assert_equal ['localhost', 27018], @conn.nodes[1]
59
+ end
51
60
  end
52
61
 
53
- should "set localhost and port correctly" do
54
- assert_equal '255.255.255.255', @conn.host
55
- assert_equal 2500, @conn.port
62
+ context "initializing with a mongodb uri" do
63
+ should "parse a simple uri" do
64
+ @conn = Connection.from_uri("mongodb://localhost", :connect => false)
65
+ assert_equal ['localhost', 27017], @conn.nodes[0]
66
+ end
67
+
68
+ should "parse a uri specifying multiple nodes" do
69
+ @conn = Connection.from_uri("mongodb://localhost:27017,mydb.com:27018", :connect => false)
70
+ assert_equal ['localhost', 27017], @conn.nodes[0]
71
+ assert_equal ['mydb.com', 27018], @conn.nodes[1]
72
+ end
73
+
74
+ should "parse a uri specifying multiple nodes with auth" do
75
+ @conn = Connection.from_uri("mongodb://kyle:s3cr3t@localhost:27017/app,mickey:m0u5e@mydb.com:27018/dsny", :connect => false)
76
+ assert_equal ['localhost', 27017], @conn.nodes[0]
77
+ assert_equal ['mydb.com', 27018], @conn.nodes[1]
78
+ auth_hash = {'username' => 'kyle', 'password' => 's3cr3t', 'db_name' => 'app'}
79
+ assert_equal auth_hash, @conn.auths[0]
80
+ auth_hash = {'username' => 'mickey', 'password' => 'm0u5e', 'db_name' => 'dsny'}
81
+ assert_equal auth_hash, @conn.auths[1]
82
+ end
83
+
84
+ should "attempt to connect" do
85
+ TCPSocket.stubs(:new).returns(new_mock_socket)
86
+ @conn = Connection.from_uri("mongodb://localhost", :connect => false)
87
+
88
+ admin_db = new_mock_db
89
+ admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
90
+ @conn.expects(:[]).with('admin').returns(admin_db)
91
+ @conn.expects(:apply_saved_authentication)
92
+ @conn.connect_to_master
93
+ end
94
+
95
+ should "raise an error on invalid uris" do
96
+ assert_raise MongoArgumentError do
97
+ Connection.from_uri("mongo://localhost", :connect => false)
98
+ end
99
+
100
+ assert_raise MongoArgumentError do
101
+ Connection.from_uri("mongodb://localhost:abc", :connect => false)
102
+ end
103
+
104
+ assert_raise MongoArgumentError do
105
+ Connection.from_uri("mongodb://localhost:27017, my.db.com:27018, ", :connect => false)
106
+ end
107
+ end
108
+
109
+ should "require all of username, password, and database if any one is specified" do
110
+ assert_raise MongoArgumentError do
111
+ Connection.from_uri("mongodb://localhost/db", :connect => false)
112
+ end
113
+
114
+ assert_raise MongoArgumentError do
115
+ Connection.from_uri("mongodb://kyle:password@localhost", :connect => false)
116
+ end
117
+ end
56
118
  end
57
119
  end
58
120
  end
59
-
@@ -1,7 +1,6 @@
1
1
  require 'test/test_helper'
2
2
 
3
- class TestCursor < Test::Unit::TestCase
4
-
3
+ class CursorTest < Test::Unit::TestCase
5
4
  context "Cursor options" do
6
5
  setup do
7
6
  @connection = stub(:class => Connection)
data/test/unit/db_test.rb CHANGED
@@ -1,97 +1,98 @@
1
1
  require 'test/test_helper'
2
2
 
3
3
  class DBTest < Test::Unit::TestCase
4
-
5
- def insert_message(db, documents)
6
- documents = [documents] unless documents.is_a?(Array)
7
- message = ByteBuffer.new
8
- message.put_int(0)
9
- BSON.serialize_cstr(message, "#{db.name}.test")
10
- documents.each { |doc| message.put_array(BSON.new.serialize(doc, true).to_a) }
11
- message = db.add_message_headers(Mongo::Constants::OP_INSERT, message)
12
- end
13
-
14
- context "DB commands" do
4
+ context "DBTest: " do
15
5
  setup do
16
- @conn = stub()
17
- @db = DB.new("testing", @conn)
18
- @collection = mock()
19
- @db.stubs(:system_command_collection).returns(@collection)
6
+ def insert_message(db, documents)
7
+ documents = [documents] unless documents.is_a?(Array)
8
+ message = ByteBuffer.new
9
+ message.put_int(0)
10
+ BSON.serialize_cstr(message, "#{db.name}.test")
11
+ documents.each { |doc| message.put_array(BSON.new.serialize(doc, true).to_a) }
12
+ message = db.add_message_headers(Mongo::Constants::OP_INSERT, message)
13
+ end
20
14
  end
21
15
 
22
- should "raise an error if given a hash with more than one key" do
23
- assert_raise MongoArgumentError do
24
- @db.command(:buildinfo => 1, :somekey => 1)
16
+ context "DB commands" do
17
+ setup do
18
+ @conn = stub()
19
+ @db = DB.new("testing", @conn)
20
+ @collection = mock()
21
+ @db.stubs(:system_command_collection).returns(@collection)
25
22
  end
26
- end
27
23
 
28
- should "raise an error if the selector is omitted" do
29
- assert_raise MongoArgumentError do
30
- @db.command({}, true)
24
+ should "raise an error if given a hash with more than one key" do
25
+ assert_raise MongoArgumentError do
26
+ @db.command(:buildinfo => 1, :somekey => 1)
27
+ end
31
28
  end
32
- end
33
29
 
34
- should "create the proper cursor" do
35
- @cursor = mock(:next_document => {"ok" => 1})
36
- Cursor.expects(:new).with(@collection, :admin => true,
37
- :limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
38
- command = {:buildinfo => 1}
39
- @db.command(command, true)
40
- end
30
+ should "raise an error if the selector is omitted" do
31
+ assert_raise MongoArgumentError do
32
+ @db.command({}, true)
33
+ end
34
+ end
41
35
 
42
- should "raise an error when the command fails" do
43
- @cursor = mock(:next_document => {"ok" => 0})
44
- Cursor.expects(:new).with(@collection, :admin => true,
45
- :limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
46
- assert_raise OperationFailure do
36
+ should "create the proper cursor" do
37
+ @cursor = mock(:next_document => {"ok" => 1})
38
+ Cursor.expects(:new).with(@collection, :admin => true,
39
+ :limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
47
40
  command = {:buildinfo => 1}
48
- @db.command(command, true, true)
41
+ @db.command(command, true)
49
42
  end
50
- end
51
43
 
52
- should "raise an error if logging out fails" do
53
- @db.expects(:command).returns({})
54
- assert_raise MongoDBError do
55
- @db.logout
44
+ should "raise an error when the command fails" do
45
+ @cursor = mock(:next_document => {"ok" => 0})
46
+ Cursor.expects(:new).with(@collection, :admin => true,
47
+ :limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
48
+ assert_raise OperationFailure do
49
+ command = {:buildinfo => 1}
50
+ @db.command(command, true, true)
51
+ end
56
52
  end
57
- end
58
53
 
59
- should "raise an error if collection creation fails" do
60
- @db.expects(:collection_names).returns([])
61
- @db.expects(:command).returns({})
62
- assert_raise MongoDBError do
63
- @db.create_collection("foo")
54
+ should "raise an error if logging out fails" do
55
+ @db.expects(:command).returns({})
56
+ assert_raise MongoDBError do
57
+ @db.logout
58
+ end
64
59
  end
65
- end
66
60
 
67
- should "raise an error if getlasterror fails" do
68
- @db.expects(:command).returns({})
69
- assert_raise MongoDBError do
70
- @db.error
61
+ should "raise an error if collection creation fails" do
62
+ @db.expects(:collection_names).returns([])
63
+ @db.expects(:command).returns({})
64
+ assert_raise MongoDBError do
65
+ @db.create_collection("foo")
66
+ end
71
67
  end
72
- end
73
68
 
74
- should "raise an error if rename fails" do
75
- @db.expects(:command).returns({})
76
- assert_raise MongoDBError do
77
- @db.rename_collection("foo", "bar")
69
+ should "raise an error if getlasterror fails" do
70
+ @db.expects(:command).returns({})
71
+ assert_raise MongoDBError do
72
+ @db.error
73
+ end
78
74
  end
79
- end
80
75
 
81
- should "raise an error if drop_index fails" do
82
- @db.expects(:command).returns({})
83
- assert_raise MongoDBError do
84
- @db.drop_index("foo", "bar")
76
+ should "raise an error if rename fails" do
77
+ @db.expects(:command).returns({})
78
+ assert_raise MongoDBError do
79
+ @db.rename_collection("foo", "bar")
80
+ end
85
81
  end
86
- end
87
82
 
88
- should "raise an error if set_profiling_level fails" do
89
- @db.expects(:command).returns({})
90
- assert_raise MongoDBError do
91
- @db.profiling_level = :slow_only
83
+ should "raise an error if drop_index fails" do
84
+ @db.expects(:command).returns({})
85
+ assert_raise MongoDBError do
86
+ @db.drop_index("foo", "bar")
87
+ end
88
+ end
89
+
90
+ should "raise an error if set_profiling_level fails" do
91
+ @db.expects(:command).returns({})
92
+ assert_raise MongoDBError do
93
+ @db.profiling_level = :slow_only
94
+ end
92
95
  end
93
96
  end
94
97
  end
95
98
  end
96
-
97
-
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.3
4
+ version: "0.19"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Menard
8
8
  - Mike Dirolf
9
+ - Kyle Banker
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
13
 
13
- date: 2010-01-25 00:00:00 -05:00
14
+ date: 2010-03-01 00:00:00 -05:00
14
15
  default_executable:
15
16
  dependencies: []
16
17
 
@@ -27,13 +28,15 @@ files:
27
28
  - Rakefile
28
29
  - mongo-ruby-driver.gemspec
29
30
  - LICENSE.txt
30
- - lib/mongo/admin.rb
31
31
  - lib/mongo/collection.rb
32
32
  - lib/mongo/connection.rb
33
33
  - lib/mongo/cursor.rb
34
34
  - lib/mongo/db.rb
35
35
  - lib/mongo/exceptions.rb
36
36
  - lib/mongo/gridfs/chunk.rb
37
+ - lib/mongo/gridfs/grid.rb
38
+ - lib/mongo/gridfs/grid_file_system.rb
39
+ - lib/mongo/gridfs/grid_io.rb
37
40
  - lib/mongo/gridfs/grid_store.rb
38
41
  - lib/mongo/gridfs.rb
39
42
  - lib/mongo/types/binary.rb
@@ -49,7 +52,6 @@ files:
49
52
  - lib/mongo/util/ordered_hash.rb
50
53
  - lib/mongo/util/server_version.rb
51
54
  - lib/mongo/util/support.rb
52
- - lib/mongo/util/xml_to_ruby.rb
53
55
  - lib/mongo.rb
54
56
  - examples/admin.rb
55
57
  - examples/capped.rb
@@ -63,8 +65,6 @@ files:
63
65
  - examples/types.rb
64
66
  - bin/bson_benchmark.rb
65
67
  - bin/fail_if_no_c.rb
66
- - bin/objectid_benchmark.rb
67
- - bin/perf.rb
68
68
  has_rdoc: true
69
69
  homepage: http://www.mongodb.org
70
70
  licenses: []
@@ -96,29 +96,33 @@ signing_key:
96
96
  specification_version: 3
97
97
  summary: Ruby driver for the MongoDB
98
98
  test_files:
99
+ - test/auxillary/authentication_test.rb
100
+ - test/auxillary/autoreconnect_test.rb
101
+ - test/binary_test.rb
102
+ - test/bson_test.rb
103
+ - test/byte_buffer_test.rb
104
+ - test/chunk_test.rb
105
+ - test/collection_test.rb
106
+ - test/connection_test.rb
107
+ - test/conversions_test.rb
108
+ - test/cursor_test.rb
109
+ - test/db_api_test.rb
110
+ - test/db_connection_test.rb
111
+ - test/db_test.rb
112
+ - test/grid_file_system_test.rb
113
+ - test/grid_io_test.rb
114
+ - test/grid_store_test.rb
115
+ - test/grid_test.rb
116
+ - test/objectid_test.rb
117
+ - test/ordered_hash_test.rb
99
118
  - test/replica/count_test.rb
100
119
  - test/replica/insert_test.rb
101
120
  - test/replica/pooled_insert_test.rb
102
121
  - test/replica/query_test.rb
103
- - test/test_admin.rb
104
- - test/test_bson.rb
105
- - test/test_byte_buffer.rb
106
- - test/test_chunk.rb
107
- - test/test_collection.rb
108
- - test/test_connection.rb
109
- - test/test_conversions.rb
110
- - test/test_cursor.rb
111
- - test/test_db.rb
112
- - test/test_db_api.rb
113
- - test/test_db_connection.rb
114
- - test/test_grid_store.rb
122
+ - test/slave_connection_test.rb
115
123
  - test/test_helper.rb
116
- - test/test_objectid.rb
117
- - test/test_ordered_hash.rb
118
- - test/test_round_trip.rb
119
- - test/test_slave_connection.rb
120
- - test/test_threading.rb
121
124
  - test/threading/test_threading_large_pool.rb
125
+ - test/threading_test.rb
122
126
  - test/unit/collection_test.rb
123
127
  - test/unit/connection_test.rb
124
128
  - test/unit/cursor_test.rb