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.
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