riak-client 1.0.0.beta → 1.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.
- data/.gitignore +7 -4
- data/Gemfile +12 -17
- data/Guardfile +1 -1
- data/LICENSE +16 -0
- data/README.markdown +178 -0
- data/RELEASE_NOTES.md +99 -0
- data/Rakefile +25 -1
- data/erl_src/riak_kv_test014_backend.beam +0 -0
- data/erl_src/riak_kv_test014_backend.erl +189 -0
- data/erl_src/riak_kv_test_backend.beam +0 -0
- data/erl_src/riak_kv_test_backend.erl +37 -19
- data/lib/riak/client.rb +322 -272
- data/lib/riak/client/beefcake_protobuffs_backend.rb +6 -10
- data/lib/riak/client/decaying.rb +28 -0
- data/lib/riak/client/excon_backend.rb +27 -11
- data/lib/riak/client/http_backend.rb +71 -2
- data/lib/riak/client/http_backend/configuration.rb +17 -3
- data/lib/riak/client/http_backend/transport_methods.rb +3 -3
- data/lib/riak/client/net_http_backend.rb +18 -14
- data/lib/riak/client/node.rb +111 -0
- data/lib/riak/client/pool.rb +180 -0
- data/lib/riak/client/protobuffs_backend.rb +15 -5
- data/lib/riak/client/search.rb +9 -3
- data/lib/riak/link.rb +5 -7
- data/lib/riak/locale/en.yml +1 -0
- data/lib/riak/node/configuration.rb +1 -0
- data/lib/riak/node/defaults.rb +19 -6
- data/lib/riak/node/generation.rb +9 -2
- data/lib/riak/node/log.rb +2 -2
- data/lib/riak/node/version.rb +22 -16
- data/lib/riak/robject.rb +19 -3
- data/lib/riak/serializers.rb +1 -1
- data/lib/riak/test_server.rb +10 -2
- data/lib/riak/version.rb +1 -1
- data/riak-client.gemspec +3 -3
- data/spec/failover/failover.rb +59 -0
- data/spec/integration/riak/http_backends_spec.rb +2 -2
- data/spec/integration/riak/node_spec.rb +16 -24
- data/spec/integration/riak/protobuffs_backends_spec.rb +1 -1
- data/spec/integration/riak/test_server_spec.rb +4 -3
- data/spec/integration/riak/threading_spec.rb +193 -0
- data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +23 -0
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +4 -2
- data/spec/riak/bucket_spec.rb +2 -1
- data/spec/riak/client_spec.rb +80 -181
- data/spec/riak/excon_backend_spec.rb +3 -2
- data/spec/riak/http_backend/configuration_spec.rb +37 -5
- data/spec/riak/http_backend/object_methods_spec.rb +1 -1
- data/spec/riak/http_backend/transport_methods_spec.rb +2 -2
- data/spec/riak/http_backend_spec.rb +53 -3
- data/spec/riak/map_reduce_spec.rb +1 -1
- data/spec/riak/net_http_backend_spec.rb +1 -2
- data/spec/riak/node_spec.rb +173 -0
- data/spec/riak/pool_spec.rb +306 -0
- data/spec/riak/robject_spec.rb +8 -4
- data/spec/riak/search_spec.rb +66 -15
- data/spec/riak/serializers_spec.rb +12 -1
- data/spec/spec_helper.rb +9 -1
- data/spec/support/http_backend_implementation_examples.rb +6 -2
- data/spec/support/sometimes.rb +46 -0
- data/spec/support/test_server.rb +50 -19
- data/spec/support/unified_backend_examples.rb +11 -10
- data/spec/support/version_filter.rb +14 -0
- metadata +40 -29
- data/lib/active_support/cache/riak_store.rb +0 -2
- data/lib/riak/cache_store.rb +0 -84
- data/lib/riak/client/pump.rb +0 -30
- data/lib/riak/util/fiber1.8.rb +0 -48
- data/spec/integration/riak/cache_store_spec.rb +0 -129
data/spec/riak/robject_spec.rb
CHANGED
@@ -214,6 +214,10 @@ describe Riak::RObject do
|
|
214
214
|
@object.conflict?.should be_false
|
215
215
|
end
|
216
216
|
|
217
|
+
it 'should return [self] for siblings' do
|
218
|
+
@object.siblings.should == [@object]
|
219
|
+
end
|
220
|
+
|
217
221
|
describe "when there are multiple values in an object" do
|
218
222
|
before :each do
|
219
223
|
response = @sample_response.dup
|
@@ -257,7 +261,7 @@ describe Riak::RObject do
|
|
257
261
|
describe "when storing the object normally" do
|
258
262
|
before :each do
|
259
263
|
@backend = mock("Backend")
|
260
|
-
@client.stub!(:backend).
|
264
|
+
@client.stub!(:backend).and_yield(@backend)
|
261
265
|
@object = Riak::RObject.new(@bucket)
|
262
266
|
@object.content_type = "text/plain"
|
263
267
|
@object.data = "This is some text."
|
@@ -278,7 +282,7 @@ describe Riak::RObject do
|
|
278
282
|
describe "when reloading the object" do
|
279
283
|
before :each do
|
280
284
|
@backend = mock("Backend")
|
281
|
-
@client.stub!(:backend).
|
285
|
+
@client.stub!(:backend).and_yield(@backend)
|
282
286
|
@object = Riak::RObject.new(@bucket, "bar")
|
283
287
|
@object.vclock = "somereallylongstring"
|
284
288
|
end
|
@@ -318,7 +322,7 @@ describe Riak::RObject do
|
|
318
322
|
describe "walking from the object to linked objects" do
|
319
323
|
before :each do
|
320
324
|
@http = mock("HTTPBackend")
|
321
|
-
@client.stub!(:http).
|
325
|
+
@client.stub!(:http).and_yield(@http)
|
322
326
|
@client.stub!(:bucket).and_return(@bucket)
|
323
327
|
@object = Riak::RObject.new(@bucket, "bar")
|
324
328
|
end
|
@@ -332,7 +336,7 @@ describe Riak::RObject do
|
|
332
336
|
describe "when deleting" do
|
333
337
|
before :each do
|
334
338
|
@backend = mock("Backend")
|
335
|
-
@client.stub!(:backend).
|
339
|
+
@client.stub!(:backend).and_yield(@backend)
|
336
340
|
@object = Riak::RObject.new(@bucket, "bar")
|
337
341
|
end
|
338
342
|
|
data/spec/riak/search_spec.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'rexml/document'
|
2
3
|
|
3
4
|
describe "Search features" do
|
4
5
|
describe Riak::Client do
|
5
6
|
before :each do
|
6
7
|
@client = Riak::Client.new
|
7
8
|
@http = mock(Riak::Client::HTTPBackend)
|
8
|
-
@client.stub!(:http).
|
9
|
+
@client.stub!(:http).and_yield(@http)
|
9
10
|
end
|
10
11
|
|
11
12
|
describe "searching" do
|
@@ -28,20 +29,40 @@ describe "Search features" do
|
|
28
29
|
describe "indexing documents" do
|
29
30
|
it "should update the default index" do
|
30
31
|
doc = {'field' => "value", 'id' => 1}
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
expect_update_body do |body|
|
33
|
+
root = REXML::Document.new(body).root
|
34
|
+
root.name.should == 'add'
|
35
|
+
root.elements.should have(1).item
|
36
|
+
root.elements[1].each_element do |el|
|
37
|
+
case el.attributes['name']
|
38
|
+
when 'field'
|
39
|
+
el.text.should == 'value'
|
40
|
+
when 'id'
|
41
|
+
el.text.should == '1'
|
42
|
+
else
|
43
|
+
fail "Spurious element in #{root.elements[1]}! #{el}"
|
44
|
+
end
|
45
|
+
end
|
35
46
|
end
|
36
47
|
@client.index(doc)
|
37
48
|
end
|
38
49
|
|
39
50
|
it "should update the specified index" do
|
40
51
|
doc = {'field' => "value", 'id' => 1}
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
52
|
+
expect_update_body(nil, "foo") do |body|
|
53
|
+
root = REXML::Document.new(body).root
|
54
|
+
root.name.should == 'add'
|
55
|
+
root.elements.should have(1).item
|
56
|
+
root.elements[1].each_element do |el|
|
57
|
+
case el.attributes['name']
|
58
|
+
when 'field'
|
59
|
+
el.text.should == 'value'
|
60
|
+
when 'id'
|
61
|
+
el.text.should == '1'
|
62
|
+
else
|
63
|
+
fail "Spurious element in #{root.elements[1]}! #{el}"
|
64
|
+
end
|
65
|
+
end
|
45
66
|
end
|
46
67
|
@client.index("foo", doc)
|
47
68
|
end
|
@@ -54,10 +75,34 @@ describe "Search features" do
|
|
54
75
|
|
55
76
|
it "should include multiple documents in the <add> request" do
|
56
77
|
docs = {'field' => "value", 'id' => 1}, {'foo' => "bar", 'id' => 2}
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
78
|
+
expect_update_body do |body|
|
79
|
+
root = REXML::Document.new(body).root
|
80
|
+
root.name.should == 'add'
|
81
|
+
docs = root.elements
|
82
|
+
docs.each do |d|
|
83
|
+
d.name.should == 'doc'
|
84
|
+
d.elements.size.should == 2
|
85
|
+
end
|
86
|
+
docs[1].each_element do |el|
|
87
|
+
case el.attributes['name']
|
88
|
+
when 'field'
|
89
|
+
el.text.should == 'value'
|
90
|
+
when 'id'
|
91
|
+
el.text.should == '1'
|
92
|
+
else
|
93
|
+
fail "Spurious element in #{docs[0]}! #{el}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
docs[2].each_element do |el|
|
97
|
+
case el.attributes['name']
|
98
|
+
when 'foo'
|
99
|
+
el.text.should == 'bar'
|
100
|
+
when 'id'
|
101
|
+
el.text.should == '2'
|
102
|
+
else
|
103
|
+
fail "Spurious element in #{docs[1]}! #{el}"
|
104
|
+
end
|
105
|
+
end
|
61
106
|
end
|
62
107
|
@client.index(*docs)
|
63
108
|
end
|
@@ -93,8 +138,14 @@ describe "Search features" do
|
|
93
138
|
end
|
94
139
|
end
|
95
140
|
|
96
|
-
def expect_update_body(body, index=nil)
|
97
|
-
|
141
|
+
def expect_update_body(body=nil, index=nil)
|
142
|
+
if block_given?
|
143
|
+
@http.should_receive(:update_search_index).with(index, kind_of(String)) do |i, b|
|
144
|
+
yield b
|
145
|
+
end
|
146
|
+
else
|
147
|
+
@http.should_receive(:update_search_index).with(index, body)
|
148
|
+
end
|
98
149
|
end
|
99
150
|
end
|
100
151
|
|
@@ -73,7 +73,7 @@ describe Riak::Serializers do
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def o.load(string)
|
76
|
-
string.sub
|
76
|
+
string.sub(/^The string is: /, '')
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
@@ -81,6 +81,7 @@ describe Riak::Serializers do
|
|
81
81
|
it 'can be registered' do
|
82
82
|
described_class['application/custom-type-1'] = custom_serializer
|
83
83
|
described_class['application/custom-type-1'].should be(custom_serializer)
|
84
|
+
# fail
|
84
85
|
end
|
85
86
|
|
86
87
|
it_behaves_like "a serializer", "application/custom-type-a", "foo", "The string is: foo" do
|
@@ -88,6 +89,16 @@ describe Riak::Serializers do
|
|
88
89
|
described_class['application/custom-type-a'] = custom_serializer
|
89
90
|
end
|
90
91
|
end
|
92
|
+
|
93
|
+
after(:each) do
|
94
|
+
# Make sure to clean up the registered serializer
|
95
|
+
%w{application/custom-type-1 application/custom-type-a}.each do |ctype|
|
96
|
+
described_class.send(:serializers).delete(ctype)
|
97
|
+
end
|
98
|
+
end
|
91
99
|
end
|
100
|
+
|
101
|
+
it_behaves_like "a serializer", "application/json; charset=UTF-8", { "a" => 7 }, %q|{"a":7}|
|
102
|
+
it_behaves_like "a serializer", "application/json ;charset=UTF-8", { "a" => 7 }, %q|{"a":7}|
|
92
103
|
end
|
93
104
|
|
data/spec/spec_helper.rb
CHANGED
@@ -10,6 +10,8 @@ require 'fakeweb'
|
|
10
10
|
Riak.disable_list_keys_warnings = true
|
11
11
|
|
12
12
|
%w[integration_setup
|
13
|
+
version_filter
|
14
|
+
sometimes
|
13
15
|
http_backend_implementation_examples
|
14
16
|
unified_backend_examples
|
15
17
|
mocks
|
@@ -20,7 +22,7 @@ Riak.disable_list_keys_warnings = true
|
|
20
22
|
end
|
21
23
|
|
22
24
|
RSpec.configure do |config|
|
23
|
-
config.debug = true
|
25
|
+
#config.debug = true
|
24
26
|
config.mock_with :rspec
|
25
27
|
|
26
28
|
config.before(:all, :integration => true) do
|
@@ -38,4 +40,10 @@ RSpec.configure do |config|
|
|
38
40
|
|
39
41
|
config.filter_run :focus => true
|
40
42
|
config.run_all_when_everything_filtered = true
|
43
|
+
|
44
|
+
if defined?(::Java)
|
45
|
+
config.seed = Time.now.utc
|
46
|
+
else
|
47
|
+
config.order = :random
|
48
|
+
end
|
41
49
|
end
|
@@ -185,7 +185,11 @@ shared_examples_for "HTTP backend" do
|
|
185
185
|
|
186
186
|
describe "SSL" do
|
187
187
|
it "should be supported" do
|
188
|
-
|
188
|
+
unless @client.http_backend == :NetHTTP
|
189
|
+
@client.nodes.each do |node|
|
190
|
+
node.http_port = $mock_server.port + 1
|
191
|
+
end
|
192
|
+
end
|
189
193
|
@client.ssl = true
|
190
194
|
setup_http_mock(:get, @backend.path("/riak/","ssl").to_s, :body => "Success!")
|
191
195
|
response = @backend.get(200, @backend.path("/riak/","ssl"))
|
@@ -194,7 +198,7 @@ shared_examples_for "HTTP backend" do
|
|
194
198
|
end
|
195
199
|
|
196
200
|
describe "HTTP Basic Authentication", :basic_auth => true do
|
197
|
-
|
201
|
+
sometimes "should add the http basic auth header" do
|
198
202
|
@client.basic_auth = "ripple:rocks"
|
199
203
|
if @client.http_backend == :NetHTTP
|
200
204
|
setup_http_mock(:get, "http://ripple:rocks@127.0.0.1:8098/riak/auth", :body => 'Success!')
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Emulates the QuickCheck ?SOMETIMES macro.
|
2
|
+
|
3
|
+
module Sometimes
|
4
|
+
def run_with_retries(example_to_run, retries)
|
5
|
+
self.example.metadata[:retries] ||= retries
|
6
|
+
retries.times do |t|
|
7
|
+
self.example.metadata[:retried] = t + 1
|
8
|
+
self.example.instance_variable_set(:@exception, nil)
|
9
|
+
example_to_run.run
|
10
|
+
break unless self.example.exception
|
11
|
+
end
|
12
|
+
if e = self.example.exception
|
13
|
+
new_exception = e.exception(e.message + "[Retried #{retries} times]")
|
14
|
+
new_exception.set_backtrace e.backtrace
|
15
|
+
self.example.instance_variable_set(:@exception, new_exception)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
RSpec.configure do |config|
|
21
|
+
config.include Sometimes
|
22
|
+
config.alias_example_to :sometimes, :sometimes => true
|
23
|
+
config.add_setting :sometimes_retry_count, :default => 3
|
24
|
+
|
25
|
+
config.around(:each, :sometimes => true) do |example|
|
26
|
+
retries = example.metadata[:retries] || RSpec.configuration.sometimes_retry_count
|
27
|
+
run_with_retries(example, retries)
|
28
|
+
end
|
29
|
+
|
30
|
+
config.after(:suite) do
|
31
|
+
formatter = RSpec.configuration.formatters.first
|
32
|
+
color = lambda {|tint, msg| formatter.send(tint, msg) }
|
33
|
+
retried_examples = RSpec.world.example_groups.map do |g|
|
34
|
+
g.descendants.map do |d|
|
35
|
+
d.filtered_examples.select {|e| e.metadata[:sometimes] && e.metadata[:retried] > 1 }
|
36
|
+
end
|
37
|
+
end.flatten
|
38
|
+
formatter.message color[retried_examples.empty? ? :green : :yellow, "\n\nRetried examples: #{retried_examples.count}"]
|
39
|
+
unless retried_examples.empty?
|
40
|
+
retried_examples.each do |e|
|
41
|
+
formatter.message " #{e.full_description}"
|
42
|
+
formatter.message(color[:yellow, " [#{e.metadata[:retried]}/#{e.metadata[:retries]}] "] + formatter.class.relative_path(e.location))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/spec/support/test_server.rb
CHANGED
@@ -1,30 +1,61 @@
|
|
1
1
|
require 'riak/test_server'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module TestServerSupport
|
4
|
+
def test_server
|
5
|
+
unless $test_server
|
6
|
+
begin
|
7
|
+
require 'yaml'
|
8
|
+
config = YAML.load_file(File.expand_path("../test_server.yml", __FILE__))
|
8
9
|
$test_server = Riak::TestServer.create(:root => config['root'],
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
:source => config['source'],
|
11
|
+
:min_port => config['min_port'] || 15000)
|
12
|
+
rescue SocketError => e
|
13
|
+
warn "Couldn't connect to Riak TestServer! #{$test_server.inspect}"
|
14
|
+
warn "Skipping remaining integration tests."
|
15
|
+
warn_crash_log
|
16
|
+
$test_server_fatal = e
|
17
|
+
rescue => e
|
18
|
+
warn "Can't run integration specs without the test server. Please create/verify spec/support/test_server.yml."
|
19
|
+
warn "Skipping remaining integration tests."
|
20
|
+
warn e.inspect
|
21
|
+
warn_crash_log
|
22
|
+
$test_server_fatal = e
|
18
23
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
end
|
25
|
+
$test_server
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_server_fatal
|
29
|
+
$test_server_fatal
|
30
|
+
end
|
31
|
+
|
32
|
+
def warn_crash_log
|
33
|
+
if $test_server && crash_log = $test_server.log + 'crash.log'
|
34
|
+
warn crash_log.read if crash_log.exist?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
RSpec.configure do |config|
|
40
|
+
config.include TestServerSupport, :integration => true
|
41
|
+
|
42
|
+
config.before(:each, :integration => true) do
|
43
|
+
fail "Test server not working: #{test_server_fatal}" if test_server_fatal
|
44
|
+
if example.metadata[:test_server] == false
|
45
|
+
test_server.stop
|
46
|
+
else
|
47
|
+
test_server.create unless test_server.exist?
|
48
|
+
test_server.start
|
22
49
|
end
|
23
50
|
end
|
24
51
|
|
25
52
|
config.after(:each, :integration => true) do
|
26
|
-
if
|
27
|
-
|
53
|
+
if test_server && !test_server_fatal && example.metadata[:test_server] != false
|
54
|
+
test_server.drop
|
28
55
|
end
|
29
56
|
end
|
57
|
+
|
58
|
+
config.after(:suite) do
|
59
|
+
$test_server.stop if $test_server
|
60
|
+
end
|
30
61
|
end
|
@@ -9,9 +9,9 @@ shared_examples_for "Unified backend API" do
|
|
9
9
|
context "fetching an object" do
|
10
10
|
before do
|
11
11
|
@robject = Riak::RObject.new(@client.bucket("test"), "fetch")
|
12
|
-
@robject.indexes['test_bin'] << 'pass'
|
13
12
|
@robject.content_type = "application/json"
|
14
13
|
@robject.data = { "test" => "pass" }
|
14
|
+
@robject.indexes['test_bin'] << 'pass' if test_server.version >= "1.0.0"
|
15
15
|
@backend.store_object(@robject)
|
16
16
|
end
|
17
17
|
|
@@ -38,15 +38,14 @@ shared_examples_for "Unified backend API" do
|
|
38
38
|
robj.data.should == { "test" => "pass" }
|
39
39
|
end
|
40
40
|
|
41
|
-
it "should accept a PR value of #{q.inspect} for the request" do
|
41
|
+
it "should accept a PR value of #{q.inspect} for the request", :version => "1.0.0" do
|
42
42
|
robj = @backend.fetch_object("test", "fetch", :pr => q)
|
43
43
|
robj.should be_kind_of(Riak::RObject)
|
44
44
|
robj.data.should == { "test" => "pass" }
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
|
49
|
-
# This really tests both storing and fetching indexes, given the setup
|
48
|
+
sometimes "should marshal indexes properly", :version => "1.0.0", :retries => 5 do
|
50
49
|
robj = @backend.fetch_object('test', 'fetch')
|
51
50
|
robj.indexes['test_bin'].should be
|
52
51
|
robj.indexes['test_bin'].should include('pass')
|
@@ -74,14 +73,16 @@ shared_examples_for "Unified backend API" do
|
|
74
73
|
@backend.reload_object(@robject, :r => q)
|
75
74
|
end
|
76
75
|
|
77
|
-
it "should accept a valid PR value of #{q.inspect} for the request" do
|
76
|
+
it "should accept a valid PR value of #{q.inspect} for the request", :version => "1.0.0" do
|
78
77
|
@backend.reload_object(@robject, :pr => q)
|
79
78
|
end
|
80
79
|
end
|
81
80
|
|
82
81
|
after do
|
83
|
-
|
84
|
-
|
82
|
+
unless example.pending?
|
83
|
+
@robject.vclock.should == @robject2.vclock
|
84
|
+
@robject.data['test'].should == "second"
|
85
|
+
end
|
85
86
|
end
|
86
87
|
end
|
87
88
|
|
@@ -112,7 +113,7 @@ shared_examples_for "Unified backend API" do
|
|
112
113
|
@backend.store_object(@robject, :returnbody => false, :w => :all, :dw => q)
|
113
114
|
end
|
114
115
|
|
115
|
-
it "should accept a PW value of #{q.inspect} for the request" do
|
116
|
+
it "should accept a PW value of #{q.inspect} for the request", :version => "1.0.0" do
|
116
117
|
@backend.store_object(@robject, :returnbody => false, :pw => q)
|
117
118
|
end
|
118
119
|
end
|
@@ -206,7 +207,7 @@ shared_examples_for "Unified backend API" do
|
|
206
207
|
@backend.list_keys("test") do |keys|
|
207
208
|
keys.each do |key|
|
208
209
|
begin
|
209
|
-
@
|
210
|
+
@client.get_object("test", key)
|
210
211
|
rescue => e
|
211
212
|
errors << e
|
212
213
|
end
|
@@ -263,7 +264,7 @@ shared_examples_for "Unified backend API" do
|
|
263
264
|
unless result.empty?
|
264
265
|
result.each do |v|
|
265
266
|
begin
|
266
|
-
@
|
267
|
+
@client.get_object("test", v['value'])
|
267
268
|
rescue => e
|
268
269
|
errors << e
|
269
270
|
end
|