riak-client 2.1.0 → 2.2.0.pre1
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.
- 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
|