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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Guardfile +15 -9
  4. data/README.markdown +118 -9
  5. data/lib/riak/bucket.rb +16 -1
  6. data/lib/riak/bucket_properties.rb +67 -0
  7. data/lib/riak/bucket_type.rb +48 -0
  8. data/lib/riak/bucket_typed/bucket.rb +113 -0
  9. data/lib/riak/client/beefcake/bucket_properties_operator.rb +178 -0
  10. data/lib/riak/client/beefcake/message_overlay.rb +42 -20
  11. data/lib/riak/client/beefcake/object_methods.rb +1 -1
  12. data/lib/riak/client/beefcake/socket.rb +6 -6
  13. data/lib/riak/client/beefcake_protobuffs_backend.rb +44 -60
  14. data/lib/riak/client/protobuffs_backend.rb +8 -8
  15. data/lib/riak/client.rb +7 -2
  16. data/lib/riak/crdt/base.rb +29 -1
  17. data/lib/riak/crdt/counter.rb +7 -3
  18. data/lib/riak/crdt/inner_flag.rb +1 -1
  19. data/lib/riak/crdt/map.rb +7 -3
  20. data/lib/riak/crdt/set.rb +7 -4
  21. data/lib/riak/errors/failed_request.rb +2 -0
  22. data/lib/riak/errors/search_error.rb +29 -0
  23. data/lib/riak/locale/en.yml +7 -0
  24. data/lib/riak/map_reduce.rb +70 -6
  25. data/lib/riak/robject.rb +19 -3
  26. data/lib/riak/search/index.rb +73 -0
  27. data/lib/riak/search/query.rb +133 -0
  28. data/lib/riak/search/result_collection.rb +91 -0
  29. data/lib/riak/search/result_document.rb +59 -0
  30. data/lib/riak/search/schema.rb +65 -0
  31. data/lib/riak/search.rb +10 -0
  32. data/lib/riak/secondary_index.rb +6 -0
  33. data/lib/riak/version.rb +1 -1
  34. data/spec/fixtures/yz_schema_template.xml +18 -0
  35. data/spec/integration/riak/bucket_types_spec.rb +218 -39
  36. data/spec/integration/riak/conflict_resolution_spec.rb +96 -0
  37. data/spec/integration/riak/crdt_spec.rb +30 -2
  38. data/spec/integration/riak/properties_spec.rb +69 -0
  39. data/spec/integration/riak/search_spec.rb +104 -0
  40. data/spec/integration/riak/secondary_index_spec.rb +18 -0
  41. data/spec/riak/beefcake_protobuffs_backend/bucket_properties_operator_spec.rb +247 -0
  42. data/spec/riak/bucket_properties_spec.rb +114 -0
  43. data/spec/riak/bucket_type_spec.rb +34 -0
  44. data/spec/riak/bucket_typed/bucket.rb +43 -0
  45. data/spec/riak/client_spec.rb +16 -8
  46. data/spec/riak/crdt/counter_spec.rb +2 -1
  47. data/spec/riak/crdt/map_spec.rb +2 -1
  48. data/spec/riak/crdt/set_spec.rb +2 -1
  49. data/spec/riak/map_reduce_spec.rb +169 -124
  50. data/spec/riak/search/index_spec.rb +62 -0
  51. data/spec/riak/search/query_spec.rb +88 -0
  52. data/spec/riak/search/result_collection_spec.rb +87 -0
  53. data/spec/riak/search/result_document_spec.rb +63 -0
  54. data/spec/riak/search/schema_spec.rb +63 -0
  55. data/spec/spec_helper.rb +2 -1
  56. data/spec/support/search_config.rb +81 -0
  57. data/spec/support/test_client.yml +14 -7
  58. metadata +44 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca7b5752fe722de4af5c0adceec72adf47c80241
4
- data.tar.gz: 0a2b4e34fefb8b5c7a9ecef6fd06d8372616ba2c
3
+ metadata.gz: 516d08bb526f4cffe8b94314f766a3424e58b884
4
+ data.tar.gz: 317c345920e67e8035662a5ecf7ab6aaf5533774
5
5
  SHA512:
6
- metadata.gz: 43600350e10122607eaca375a69225285a28403041e231d3bf527c04c90fedde75fc7136f7bd626d3ea448e0ecfd4deed10e8707e6a12605077662edb72a60f0
7
- data.tar.gz: 1b3e910ec530a8d9e41a333b376918608aac4398be8b15af478978323e22f94cd407e008a43135c2f4bb12914e4e1ea998d88ac6acf19201242c3bd9eac6e0e9
6
+ metadata.gz: 9b251487856bf8dfba41427a8f1370a97541662d47c5047ee5d6a5f7f2b7567630a32b741cd821ce2320adae76ca4e334672f6fef267e7b7117941b1b77c1d0a
7
+ data.tar.gz: f4da60a9d5a7254e9541fa87e97fd32acd67da05a5024d972cb23e23ff1e30d93601c132266d29200eb907bdbcc9141495b6b7d490ea5f8f7f3ab3559dbc3ce6
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gemspec
5
5
  group :guard do
6
6
  gem 'guard-rspec'
7
7
  gem 'rb-fsevent'
8
- gem 'growl'
8
+ gem 'terminal-notifier-guard'
9
9
  end
10
10
 
11
11
  platforms :mri do
@@ -15,3 +15,5 @@ end
15
15
  platforms :jruby, :rbx do
16
16
  gem 'json'
17
17
  end
18
+
19
+ gem 'beefcake', '1.1.0.pre1'
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
- rvms = %w[ 1.8.7 1.9.2 1.9.3 ].map do |version|
7
- "#{version}@#{gemset}"
8
- end
9
-
10
- guard 'rspec', :cli => '--profile --tag "~slow"', :rvm => rvms do
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('spec/spec_helper.rb') { "spec/riak" }
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](http://docs.basho.com/riak/latest/dev/advanced/bucket-types/) to
100
- enable groups of similar buckets to share properties, configuration, and to namespace values
101
- within those buckets. Bucket type support is integral to how CRDTs work.
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
- Many operations take `type` options to perform them with a specific bucket type.
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
- bucket = client.bucket 'pizzas'
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
- client.create_search_index 'pizzas'
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
- result = client.search('pizzas', 'toppings_ss:ham') # Returns a results hash
169
- result['num_found'] # total number of results
170
- result['docs'] # the list of indexed documents
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}}#{" keys=[#{keys.join(',')}]" if defined?(@keys)}>"
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