riak-client 2.1.0 → 2.2.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/Guardfile +15 -9
- data/README.markdown +118 -9
- data/lib/riak/bucket.rb +16 -1
- data/lib/riak/bucket_properties.rb +67 -0
- data/lib/riak/bucket_type.rb +48 -0
- data/lib/riak/bucket_typed/bucket.rb +113 -0
- data/lib/riak/client/beefcake/bucket_properties_operator.rb +178 -0
- data/lib/riak/client/beefcake/message_overlay.rb +42 -20
- data/lib/riak/client/beefcake/object_methods.rb +1 -1
- data/lib/riak/client/beefcake/socket.rb +6 -6
- data/lib/riak/client/beefcake_protobuffs_backend.rb +44 -60
- data/lib/riak/client/protobuffs_backend.rb +8 -8
- data/lib/riak/client.rb +7 -2
- data/lib/riak/crdt/base.rb +29 -1
- data/lib/riak/crdt/counter.rb +7 -3
- data/lib/riak/crdt/inner_flag.rb +1 -1
- data/lib/riak/crdt/map.rb +7 -3
- data/lib/riak/crdt/set.rb +7 -4
- data/lib/riak/errors/failed_request.rb +2 -0
- data/lib/riak/errors/search_error.rb +29 -0
- data/lib/riak/locale/en.yml +7 -0
- data/lib/riak/map_reduce.rb +70 -6
- data/lib/riak/robject.rb +19 -3
- data/lib/riak/search/index.rb +73 -0
- data/lib/riak/search/query.rb +133 -0
- data/lib/riak/search/result_collection.rb +91 -0
- data/lib/riak/search/result_document.rb +59 -0
- data/lib/riak/search/schema.rb +65 -0
- data/lib/riak/search.rb +10 -0
- data/lib/riak/secondary_index.rb +6 -0
- data/lib/riak/version.rb +1 -1
- data/spec/fixtures/yz_schema_template.xml +18 -0
- data/spec/integration/riak/bucket_types_spec.rb +218 -39
- data/spec/integration/riak/conflict_resolution_spec.rb +96 -0
- data/spec/integration/riak/crdt_spec.rb +30 -2
- data/spec/integration/riak/properties_spec.rb +69 -0
- data/spec/integration/riak/search_spec.rb +104 -0
- data/spec/integration/riak/secondary_index_spec.rb +18 -0
- data/spec/riak/beefcake_protobuffs_backend/bucket_properties_operator_spec.rb +247 -0
- data/spec/riak/bucket_properties_spec.rb +114 -0
- data/spec/riak/bucket_type_spec.rb +34 -0
- data/spec/riak/bucket_typed/bucket.rb +43 -0
- data/spec/riak/client_spec.rb +16 -8
- data/spec/riak/crdt/counter_spec.rb +2 -1
- data/spec/riak/crdt/map_spec.rb +2 -1
- data/spec/riak/crdt/set_spec.rb +2 -1
- data/spec/riak/map_reduce_spec.rb +169 -124
- data/spec/riak/search/index_spec.rb +62 -0
- data/spec/riak/search/query_spec.rb +88 -0
- data/spec/riak/search/result_collection_spec.rb +87 -0
- data/spec/riak/search/result_document_spec.rb +63 -0
- data/spec/riak/search/schema_spec.rb +63 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/search_config.rb +81 -0
- data/spec/support/test_client.yml +14 -7
- metadata +44 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 516d08bb526f4cffe8b94314f766a3424e58b884
|
4
|
+
data.tar.gz: 317c345920e67e8035662a5ecf7ab6aaf5533774
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b251487856bf8dfba41427a8f1370a97541662d47c5047ee5d6a5f7f2b7567630a32b741cd821ce2320adae76ca4e334672f6fef267e7b7117941b1b77c1d0a
|
7
|
+
data.tar.gz: f4da60a9d5a7254e9541fa87e97fd32acd67da05a5024d972cb23e23ff1e30d93601c132266d29200eb907bdbcc9141495b6b7d490ea5f8f7f3ab3559dbc3ce6
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,14 +1,20 @@
|
|
1
1
|
# A sample Guardfile
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
3
|
-
gemset = ENV['RVM_GEMSET'] || 'ripple'
|
4
|
-
gemset = "@#{gemset}" unless gemset.to_s == ''
|
5
3
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
5
|
+
# rspec may be run, below are examples of the most common uses.
|
6
|
+
# * bundler: 'bundle exec rspec'
|
7
|
+
# * bundler binstubs: 'bin/rspec'
|
8
|
+
# * spring: 'bin/rsspec' (This will use spring if running and you have
|
9
|
+
# installed the spring binstubs per the docs)
|
10
|
+
# * zeus: 'zeus rspec' (requires the server to be started separetly)
|
11
|
+
# * 'just' rspec: 'rspec'
|
12
|
+
guard :rspec, cmd: 'bundle exec rspec', all_after_pass: true, all_on_start: true do
|
11
13
|
watch(%r{^spec/.+_spec\.rb$})
|
12
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
13
|
-
watch(
|
14
|
+
watch(%r{^lib/riak/(.+)\.rb$}) { |m| "spec/riak/#{m[1]}_spec.rb" }
|
15
|
+
watch(%r{^lib/riak/client/}) { 'spec/riak/beefcake_protobuffs_backend' }
|
16
|
+
watch('spec/spec_helper.rb') { "spec" }
|
17
|
+
|
18
|
+
watch(/^spec\/integration/) { 'spec:integration' }
|
14
19
|
end
|
20
|
+
|
data/README.markdown
CHANGED
@@ -96,15 +96,40 @@ new_one.store
|
|
96
96
|
|
97
97
|
## Bucket Types
|
98
98
|
|
99
|
-
Riak 2 uses [bucket types]
|
100
|
-
|
101
|
-
|
99
|
+
Riak 2 uses [bucket types][1] to enable groups of similar buckets to share
|
100
|
+
properties, configuration, and to namespace values within those buckets. Bucket
|
101
|
+
type support is integral to how CRDTs work.
|
102
102
|
|
103
|
-
|
103
|
+
[1]: http://docs.basho.com/riak/latest/dev/advanced/bucket-types
|
104
|
+
|
105
|
+
In versions 2.2.0 and newer of this client, bucket types have a first-class
|
106
|
+
representation, and can be used to create `BucketTyped::Bucket` objects that are
|
107
|
+
namespaced differently from regular `Riak::Bucket` objects.
|
104
108
|
|
105
109
|
```ruby
|
106
110
|
# This example assumes you have a "beverages" bucket type.
|
111
|
+
beverages = client.bucket_type 'beverages'
|
112
|
+
|
113
|
+
coffees = beverages.bucket 'coffees'
|
114
|
+
untyped_coffees = client.bucket 'coffees'
|
115
|
+
|
116
|
+
chapadao = coffees.new 'chapadao'
|
117
|
+
chapadao.data = "Chapadao de Ferro"
|
118
|
+
chapadao.store # stores this in the "beverages" bucket type
|
119
|
+
|
120
|
+
untyped_coffees.get 'chapadao' # raises error, not found
|
121
|
+
coffees.get 'chapadao' # succeeds
|
122
|
+
|
123
|
+
chapadao.reload # succeeds
|
124
|
+
|
125
|
+
untyped_coffees.delete 'chapadao' # silently fails to delete it
|
126
|
+
chapadao.delete # deletes it
|
127
|
+
coffees.delete 'chapadao' # deletes it
|
128
|
+
```
|
107
129
|
|
130
|
+
Client 2.0 and 2.1 code that uses the `type` argument to methods still works:
|
131
|
+
|
132
|
+
```ruby
|
108
133
|
coffees = client.bucket 'coffees'
|
109
134
|
|
110
135
|
chapadao = coffees.new 'chapadao'
|
@@ -148,11 +173,14 @@ This documentation assumes there's a `yokozuna` bucket type created and activate
|
|
148
173
|
``` ruby
|
149
174
|
# Create a client and bucket.
|
150
175
|
client = Riak::Client.new
|
151
|
-
|
176
|
+
bucket_type = client.bucket_type 'yokozuna'
|
177
|
+
bucket = bucket_type.bucket 'pizzas'
|
152
178
|
|
153
179
|
# Create an index and add it to a typed bucket. Setting the index on the bucket
|
154
180
|
# may fail until the index creation has propagated.
|
155
|
-
|
181
|
+
index = Riak::Search::Index.new 'pizzas'
|
182
|
+
index.exist? #=> false
|
183
|
+
index.create!
|
156
184
|
client.set_bucket_props bucket, {search_index: 'pizzas'}, 'yokozuna'
|
157
185
|
|
158
186
|
# Store some records for indexing
|
@@ -165,9 +193,18 @@ hawaiian.data = {toppings_ss: %w{ham pineapple}}
|
|
165
193
|
hawaiian.store type: 'yokozuna'
|
166
194
|
|
167
195
|
# Search the pizzas index for hashes that have a "ham" entry in the toppings_ss array
|
168
|
-
|
169
|
-
|
170
|
-
|
196
|
+
query = Riak::Search::Query.new client, index, 'toppings_ss:ham'
|
197
|
+
query.rows = 5 # return the first five pizzas
|
198
|
+
|
199
|
+
result = query.results # returns a ResultsCollection object
|
200
|
+
result.length # number of results returned
|
201
|
+
result.num_found # total number of results found, including ones not returned
|
202
|
+
|
203
|
+
pizza_result = result.first # a ResultDocument of the first pizza
|
204
|
+
pizza_result.score # score of the match
|
205
|
+
pizza_result.key # also pizza.bucket and pizza.bucket_type
|
206
|
+
|
207
|
+
pizza = pizza_result.robject # load the actual RObject for the match
|
171
208
|
```
|
172
209
|
|
173
210
|
## Secondary Index Examples
|
@@ -482,6 +519,78 @@ The payload for each event contains the following keys:
|
|
482
519
|
* `:client_id`: The client_id of the Riak client
|
483
520
|
* `:_method_args`: The array of method arguments for the instrumented method. For instance, for `riak.get_object`, this value would resemble `[<Riak::Bucket ...>, 'key', {}]`
|
484
521
|
|
522
|
+
## Running Specs
|
523
|
+
|
524
|
+
We aim to have a comprehensive and fast set of tests, implemented using a modern,
|
525
|
+
well-supported version of RSpec. These tests include both unit specs for
|
526
|
+
individual classes, and integration specs that ensure the client works properly
|
527
|
+
with an actual Riak instance.
|
528
|
+
|
529
|
+
The [Riak Ruby Vagrant][1] virtual machine's Riak configuration is normally
|
530
|
+
used to test this client in development. Once it's up and running, configure
|
531
|
+
the Ruby `test_client.yml` on the host machine to connect to `pb_port: 17017`
|
532
|
+
and test away.
|
533
|
+
|
534
|
+
[1]: https://github.com/basho-labs/riak-ruby-vagrant
|
535
|
+
|
536
|
+
Configuring the Riak node the tests connect to is done via the
|
537
|
+
`spec/support/test_client.yml` file, which is loaded into a Ruby hash with
|
538
|
+
symbolized keys, and passed to `Riak::Client.new`.
|
539
|
+
|
540
|
+
```yml
|
541
|
+
# test_client.yml
|
542
|
+
pb_port: 10017
|
543
|
+
# UNCOMMENT AUTHENTICATION SECTION WHEN RIAK HAS SECURITY ENABLED
|
544
|
+
# authentication:
|
545
|
+
# user: user
|
546
|
+
# password: password
|
547
|
+
# ca_file: spec/support/certs/ca.crt
|
548
|
+
```
|
549
|
+
|
550
|
+
### Spec dependencies
|
551
|
+
|
552
|
+
Specs depend on the following Riak configurations:
|
553
|
+
|
554
|
+
* The **LevelDB backend** is necessary for testing secondary indexes.
|
555
|
+
* **allow_mult** is required for many features: conflict resolution, and legacy
|
556
|
+
counters among them.
|
557
|
+
* **Riak Search 2.0** ("Yokozuna") must be configured for testing full-text
|
558
|
+
search.
|
559
|
+
|
560
|
+
The following bucket types are used during testing:
|
561
|
+
|
562
|
+
```shell
|
563
|
+
riak-admin bucket-type create counters '{"props":{"datatype":"counter", "allow_mult":true}}'
|
564
|
+
riak-admin bucket-type create other_counters '{"props":{"datatype":"counter", "allow_mult":true}}'
|
565
|
+
riak-admin bucket-type create maps '{"props":{"datatype":"map", "allow_mult":true}}'
|
566
|
+
riak-admin bucket-type create sets '{"props":{"datatype":"set", "allow_mult":true}}'
|
567
|
+
riak-admin bucket-type create yokozuna '{"props":{}}'
|
568
|
+
|
569
|
+
riak-admin bucket-type activate other_counters
|
570
|
+
riak-admin bucket-type activate counters
|
571
|
+
riak-admin bucket-type activate maps
|
572
|
+
riak-admin bucket-type activate sets
|
573
|
+
riak-admin bucket-type activate yokozuna
|
574
|
+
```
|
575
|
+
|
576
|
+
Client tests run both with and without security enabled, as we have to test
|
577
|
+
several positive and negative paths. The tests broadly depend on these users
|
578
|
+
and roles:
|
579
|
+
|
580
|
+
```shell
|
581
|
+
riak-admin security add-user user password=password
|
582
|
+
riak-admin security add-user certuser
|
583
|
+
|
584
|
+
riak-admin security add-source user 0.0.0.0/0 password
|
585
|
+
riak-admin security add-source certuser 0.0.0.0/0 certificate
|
586
|
+
|
587
|
+
riak-admin security grant riak_kv.get,riak_kv.put,riak_kv.delete,\
|
588
|
+
riak_kv.index,riak_kv.list_keys,riak_kv.list_buckets,\
|
589
|
+
riak_core.get_bucket,riak_core.set_bucket,\
|
590
|
+
riak_core.get_bucket_type,riak_core.set_bucket_type,\
|
591
|
+
search.admin,search.query,riak_kv.mapreduce on any to user
|
592
|
+
```
|
593
|
+
|
485
594
|
## How to Contribute
|
486
595
|
|
487
596
|
* Fork the project on [Github](http://github.com/basho/riak-ruby-client). If you have already forked, use `git pull --rebase` to reapply your changes on top of the mainline. Example:
|
data/lib/riak/bucket.rb
CHANGED
@@ -239,7 +239,22 @@ module Riak
|
|
239
239
|
|
240
240
|
# @return [String] a representation suitable for IRB and debugging output
|
241
241
|
def inspect
|
242
|
-
"#<Riak::Bucket {#{name}}
|
242
|
+
"#<Riak::Bucket {#{name}}>"
|
243
|
+
end
|
244
|
+
|
245
|
+
# Pretty prints the bucket for `pp` or `pry`.
|
246
|
+
def pretty_print(pp)
|
247
|
+
pp.object_group self do
|
248
|
+
pp.breakable
|
249
|
+
pp.text "name=#{name}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# Does this {Bucket} have a non-default bucket type? {BucketTyped::Bucket}
|
254
|
+
# instances with non-default types return `true`.
|
255
|
+
# @return [Boolean] false
|
256
|
+
def needs_type?
|
257
|
+
false
|
243
258
|
end
|
244
259
|
|
245
260
|
# @return [true,false] whether the other is equivalent
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'riak'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
# Provides a predictable and useful interface to bucket properties. Allows
|
5
|
+
# reading, reloading, and setting new values for bucket properties.
|
6
|
+
class BucketProperties
|
7
|
+
attr_reader :client
|
8
|
+
attr_reader :bucket
|
9
|
+
|
10
|
+
# Create a properties object for a bucket (including bucket-typed buckets).
|
11
|
+
# @param [Riak::Bucket, Riak::BucketTyped::Bucket] bucket
|
12
|
+
def initialize(bucket)
|
13
|
+
@bucket = bucket
|
14
|
+
@client = bucket.client
|
15
|
+
end
|
16
|
+
|
17
|
+
# Clobber the cached properties, and reload them from Riak.
|
18
|
+
def reload
|
19
|
+
@cached_props = nil
|
20
|
+
cached_props
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# Write bucket properties and invalidate the cache in this object.
|
25
|
+
def store
|
26
|
+
client.backend do |be|
|
27
|
+
be.bucket_properties_operator.put bucket, cached_props
|
28
|
+
end
|
29
|
+
@cached_props = nil
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Take bucket properties from a given {Hash} or {Riak::BucketProperties}
|
34
|
+
# object.
|
35
|
+
# @param [Hash<String, Object>, Riak::BucketProperties] other
|
36
|
+
def merge!(other)
|
37
|
+
cached_props.merge! other
|
38
|
+
end
|
39
|
+
|
40
|
+
# Convert the cached properties into a hash for merging.
|
41
|
+
# @return [Hash<String, Object>] the bucket properties in a {Hash}
|
42
|
+
def to_hash
|
43
|
+
cached_props
|
44
|
+
end
|
45
|
+
|
46
|
+
# Read a bucket property
|
47
|
+
# @param [String] property_name
|
48
|
+
# @return [Object] the bucket property's value
|
49
|
+
def [](property_name)
|
50
|
+
cached_props[property_name.to_s]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Write a bucket property
|
54
|
+
# @param [String] property_name
|
55
|
+
# @param [Object] value
|
56
|
+
def []=(property_name, value)
|
57
|
+
cached_props[property_name.to_s] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def cached_props
|
62
|
+
@cached_props ||= client.backend do |be|
|
63
|
+
be.bucket_properties_operator.get bucket
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'riak/bucket_typed/bucket'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
# A representation of a bucket type
|
5
|
+
class BucketType
|
6
|
+
attr_reader :client, :name
|
7
|
+
|
8
|
+
# The name of Riak's default bucket type.
|
9
|
+
DEFAULT_NAME = 'default'
|
10
|
+
|
11
|
+
# Create a bucket type object manually.
|
12
|
+
# @param [Client] client the {Riak::Client} for this bucket type
|
13
|
+
# @param [String] name the name of this bucket type
|
14
|
+
def initialize(client, name)
|
15
|
+
@client, @name = client, name
|
16
|
+
end
|
17
|
+
|
18
|
+
# Is this bucket type the default?
|
19
|
+
# @return [Boolean]
|
20
|
+
def default?
|
21
|
+
name == DEFAULT_NAME
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get a bucket of this type
|
25
|
+
# @param [String] bucket_name the name of this bucket
|
26
|
+
def bucket(bucket_name)
|
27
|
+
BucketTyped::Bucket.new client, bucket_name, self
|
28
|
+
end
|
29
|
+
|
30
|
+
# Pretty prints the bucket for `pp` or `pry`.
|
31
|
+
def pretty_print(pp)
|
32
|
+
pp.object_group self do
|
33
|
+
pp.breakable
|
34
|
+
pp.text "name=#{name}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get the properties of this bucket type
|
39
|
+
# @return [Hash<Symbol,Object>]
|
40
|
+
def properties
|
41
|
+
@properties ||= client.backend do |be|
|
42
|
+
be.get_bucket_type_props name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
alias :props :properties
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'riak/bucket'
|
2
|
+
require 'riak/bucket_type'
|
3
|
+
|
4
|
+
module Riak
|
5
|
+
|
6
|
+
# Container module for subclasses of objects with bucket type data attached.
|
7
|
+
# Currently only used for {BucketTyped::Bucket}.
|
8
|
+
module BucketTyped
|
9
|
+
|
10
|
+
# A bucket that has a {BucketType} attached to it. Normally created using
|
11
|
+
# the {BucketType#bucket} method. Inherits most of its behavior from the
|
12
|
+
# {Riak::Bucket} class.
|
13
|
+
class Bucket < Riak::Bucket
|
14
|
+
|
15
|
+
# @return [BucketType] the bucket type used with this bucket
|
16
|
+
attr_reader :type
|
17
|
+
|
18
|
+
# Create a bucket-typed bucket manually.
|
19
|
+
# @param [Client] client the {Riak::Client} for this bucket
|
20
|
+
# @param [String] name the name of this bucket
|
21
|
+
# @param [BucketType,String] type the bucket type of this bucket
|
22
|
+
def initialize(client, name, type)
|
23
|
+
if type.is_a? String
|
24
|
+
type = client.bucket_type type
|
25
|
+
elsif !(type.is_a? BucketType)
|
26
|
+
raise ArgumentError, t('argument_error.bucket_type', bucket_type: type)
|
27
|
+
end
|
28
|
+
|
29
|
+
@type = type
|
30
|
+
|
31
|
+
super client, name
|
32
|
+
end
|
33
|
+
|
34
|
+
# Retrieve an object from within the bucket type and bucket.
|
35
|
+
# @param [String] key the key of the object to retrieve
|
36
|
+
# @param [Hash] options query parameters for the request
|
37
|
+
# @option options [Fixnum] :r - the read quorum for the request - how many nodes should concur on the read
|
38
|
+
# @return [Riak::RObject] the object
|
39
|
+
# @raise [FailedRequest] if the object is not found or some other error occurs
|
40
|
+
def get(key, options={ })
|
41
|
+
super key, o(options)
|
42
|
+
end
|
43
|
+
alias :[] :get
|
44
|
+
|
45
|
+
# Deletes a key from the bucket
|
46
|
+
# @param [String] key the key to delete
|
47
|
+
# @param [Hash] options quorum options
|
48
|
+
# @option options [Fixnum] :rw - the read/write quorum for the
|
49
|
+
# delete
|
50
|
+
# @option options [String] :vclock - the vector clock of the
|
51
|
+
# object being deleted
|
52
|
+
def delete(key, options={ })
|
53
|
+
super key, o(options)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Retrieves a list of keys in this bucket.
|
57
|
+
# If a block is given, keys will be streamed through
|
58
|
+
# the block (useful for large buckets). When streaming,
|
59
|
+
# results of the operation will not be returned to the caller.
|
60
|
+
# @yield [Array<String>] a list of keys from the current chunk
|
61
|
+
# @return [Array<String>] Keys in this bucket
|
62
|
+
# @note This operation has serious performance implications and
|
63
|
+
# should not be used in production applications.
|
64
|
+
def keys(options={ }, &block)
|
65
|
+
super o(options), &block
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [String] a friendly representation of this bucket-typed bucket
|
69
|
+
def inspect
|
70
|
+
"#<Riak::BucketTyped::Bucket {#{ type.name }/#{ name }}>"
|
71
|
+
end
|
72
|
+
|
73
|
+
def props=(new_props)
|
74
|
+
raise ArgumentError, t('hash_type', hash: new_props.inspect) unless new_props.is_a? Hash
|
75
|
+
complete_props = props.merge new_props
|
76
|
+
@client.set_bucket_props(self, complete_props, self.type.name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def props
|
80
|
+
@props ||= @client.get_bucket_props(self, type: self.type.name)
|
81
|
+
end
|
82
|
+
|
83
|
+
def clear_props
|
84
|
+
@props = nil
|
85
|
+
@client.clear_bucket_props(self, type: self.type.name)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Pretty prints the bucket for `pp` or `pry`.
|
89
|
+
def pretty_print(pp)
|
90
|
+
pp.object_group self do
|
91
|
+
pp.breakable
|
92
|
+
pp.text "bucket_type="
|
93
|
+
type.pretty_print(pp)
|
94
|
+
pp.breakable
|
95
|
+
pp.text "name=#{name}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Does this {BucketTyped::Bucket} have a non-default bucket type?
|
100
|
+
# @return [Boolean] true if this bucket has a non-default type.
|
101
|
+
def needs_type?
|
102
|
+
return true unless type.default?
|
103
|
+
return false
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
# merge in the type name with options
|
108
|
+
def o(options)
|
109
|
+
{ type: type.name }.merge options
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
class Riak::Client::BeefcakeProtobuffsBackend
|
2
|
+
def bucket_properties_operator
|
3
|
+
BucketPropertiesOperator.new(self)
|
4
|
+
end
|
5
|
+
|
6
|
+
|
7
|
+
class BucketPropertiesOperator
|
8
|
+
attr_reader :backend
|
9
|
+
|
10
|
+
QUORUMS = Riak::Client::ProtobuffsBackend::QUORUMS
|
11
|
+
|
12
|
+
def initialize(backend)
|
13
|
+
@backend = backend
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(bucket, options={})
|
17
|
+
response = backend.protocol do |p|
|
18
|
+
p.write :GetBucketReq, get_request(bucket, options)
|
19
|
+
p.expect :GetBucketResp, RpbGetBucketResp
|
20
|
+
end
|
21
|
+
|
22
|
+
properties = response.props.to_hash.stringify_keys
|
23
|
+
|
24
|
+
return rubyfy(properties)
|
25
|
+
end
|
26
|
+
|
27
|
+
def put(bucket, props={}, options={})
|
28
|
+
properties = riakify props
|
29
|
+
|
30
|
+
request = put_request bucket, properties, options
|
31
|
+
|
32
|
+
backend.protocol do |p|
|
33
|
+
p.write :SetBucketReq, request
|
34
|
+
p.expect :SetBucketResp
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def rubyfy(received_properties)
|
40
|
+
props = received_properties.dup
|
41
|
+
|
42
|
+
rubyfy_quorums(props)
|
43
|
+
rubyfy_hooks(props)
|
44
|
+
rubyfy_modfuns(props)
|
45
|
+
|
46
|
+
return props
|
47
|
+
end
|
48
|
+
|
49
|
+
def riakify(requested_properties)
|
50
|
+
props = requested_properties.stringify_keys
|
51
|
+
|
52
|
+
riakify_quorums(props)
|
53
|
+
riakify_hooks(props)
|
54
|
+
riakify_modfuns(props)
|
55
|
+
riakify_repl_mode(props)
|
56
|
+
|
57
|
+
return props
|
58
|
+
end
|
59
|
+
|
60
|
+
def rubyfy_quorums(props)
|
61
|
+
%w{r pr w pw dw rw}.each do |k|
|
62
|
+
next unless props[k]
|
63
|
+
next unless QUORUMS.values.include? props[k]
|
64
|
+
|
65
|
+
props[k] = QUORUMS.invert[props[k]]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def riakify_quorums(props)
|
70
|
+
%w{r pr w pw dw rw}.each do |k|
|
71
|
+
next unless props[k]
|
72
|
+
v = props[k].to_s
|
73
|
+
next unless QUORUMS.keys.include? v
|
74
|
+
|
75
|
+
props[k] = QUORUMS[v]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def rubyfy_hooks(props)
|
80
|
+
%w{precommit postcommit}.each do |k|
|
81
|
+
next unless props[k]
|
82
|
+
props[k] = props[k].map do |v|
|
83
|
+
next v[:name] if v[:name]
|
84
|
+
rubyfy_modfun(v[:modfun])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def riakify_hooks(props)
|
90
|
+
%w{precommit postcommit}.each do |k|
|
91
|
+
next unless v = props[k]
|
92
|
+
|
93
|
+
if v.is_a? Array
|
94
|
+
props[k] = v.map{ |e| riakify_single_hook(e) }
|
95
|
+
else
|
96
|
+
props[k] = [riakify_single_hook(v)]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def riakify_single_hook(hook)
|
102
|
+
message = RpbCommitHook.new
|
103
|
+
|
104
|
+
if hook.is_a? String
|
105
|
+
message.name = hook
|
106
|
+
elsif hook['name']
|
107
|
+
message.name = hook['name']
|
108
|
+
else
|
109
|
+
message.modfun = riakify_modfun(hook)
|
110
|
+
end
|
111
|
+
return message
|
112
|
+
end
|
113
|
+
|
114
|
+
def rubyfy_modfuns(props)
|
115
|
+
%w{chash_keyfun linkfun}.each do |k|
|
116
|
+
next if props[k].nil?
|
117
|
+
props[k] = rubyfy_modfun(props[k])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def riakify_modfuns(props)
|
122
|
+
%w{chash_keyfun linkfun}.each do |k|
|
123
|
+
next if props[k].nil?
|
124
|
+
props[k] = riakify_modfun(props[k])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def rubyfy_modfun(modfun)
|
129
|
+
{
|
130
|
+
'mod' => modfun[:module],
|
131
|
+
'fun' => modfun[:function]
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
def riakify_modfun(modfun)
|
136
|
+
m = modfun.stringify_keys
|
137
|
+
RpbModFun.new(module: m['mod'], function: m['fun'])
|
138
|
+
end
|
139
|
+
|
140
|
+
def riakify_repl_mode(props)
|
141
|
+
return unless props['repl'].is_a? Symbol
|
142
|
+
|
143
|
+
props['repl'] = case props['repl']
|
144
|
+
when :false
|
145
|
+
0
|
146
|
+
when :realtime
|
147
|
+
1
|
148
|
+
when :fullsync
|
149
|
+
2
|
150
|
+
when :true
|
151
|
+
3
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def name_options(bucket)
|
156
|
+
o = {}
|
157
|
+
if bucket.is_a? Riak::Bucket
|
158
|
+
o[:bucket] = bucket.name
|
159
|
+
o[:type] = bucket.type.name if bucket.needs_type?
|
160
|
+
else
|
161
|
+
o[:bucket] = bucket
|
162
|
+
end
|
163
|
+
|
164
|
+
return o
|
165
|
+
end
|
166
|
+
|
167
|
+
def get_request(bucket, options)
|
168
|
+
RpbGetBucketReq.new options.merge name_options(bucket)
|
169
|
+
end
|
170
|
+
|
171
|
+
def put_request(bucket, props, options)
|
172
|
+
req_options = options.merge name_options(bucket)
|
173
|
+
req_options[:props] = RpbBucketProps.new props.symbolize_keys
|
174
|
+
|
175
|
+
RpbSetBucketReq.new req_options
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|