riak-client 2.2.0.pre1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +0 -2
  4. data/README.markdown +11 -7
  5. data/RELEASE_NOTES.md +29 -2
  6. data/Rakefile +5 -3
  7. data/lib/riak/bucket.rb +55 -23
  8. data/lib/riak/bucket_properties.rb +8 -1
  9. data/lib/riak/bucket_type.rb +29 -0
  10. data/lib/riak/bucket_typed/bucket.rb +15 -7
  11. data/lib/riak/client.rb +24 -8
  12. data/lib/riak/client/beefcake/bucket_properties_operator.rb +8 -8
  13. data/lib/riak/client/beefcake/crdt/set_loader.rb +1 -1
  14. data/lib/riak/client/beefcake/crdt_loader.rb +1 -1
  15. data/lib/riak/client/beefcake/crdt_operator.rb +9 -9
  16. data/lib/riak/client/beefcake/message_codes.rb +4 -0
  17. data/lib/riak/client/beefcake/message_overlay.rb +4 -0
  18. data/lib/riak/client/beefcake/messages.rb +35 -5
  19. data/lib/riak/client/beefcake/object_methods.rb +21 -13
  20. data/lib/riak/client/beefcake/protocol.rb +7 -7
  21. data/lib/riak/client/beefcake/socket.rb +2 -2
  22. data/lib/riak/client/beefcake_protobuffs_backend.rb +58 -33
  23. data/lib/riak/client/protobuffs_backend.rb +5 -5
  24. data/lib/riak/client/yokozuna.rb +3 -3
  25. data/lib/riak/core_ext/deep_dup.rb +1 -1
  26. data/lib/riak/counter.rb +10 -10
  27. data/lib/riak/crdt/base.rb +39 -21
  28. data/lib/riak/crdt/batch_counter.rb +5 -5
  29. data/lib/riak/crdt/batch_map.rb +2 -2
  30. data/lib/riak/crdt/counter.rb +7 -7
  31. data/lib/riak/crdt/inner_counter.rb +4 -4
  32. data/lib/riak/crdt/inner_flag.rb +3 -3
  33. data/lib/riak/crdt/inner_map.rb +1 -1
  34. data/lib/riak/crdt/inner_register.rb +1 -1
  35. data/lib/riak/crdt/inner_set.rb +5 -5
  36. data/lib/riak/crdt/map.rb +9 -9
  37. data/lib/riak/crdt/set.rb +10 -10
  38. data/lib/riak/crdt/typed_collection.rb +39 -36
  39. data/lib/riak/errors/base.rb +1 -1
  40. data/lib/riak/errors/crdt_error.rb +20 -0
  41. data/lib/riak/errors/search_error.rb +6 -0
  42. data/lib/riak/index_collection.rb +1 -1
  43. data/lib/riak/link.rb +5 -3
  44. data/lib/riak/locale/en.yml +5 -1
  45. data/lib/riak/map_reduce.rb +7 -7
  46. data/lib/riak/map_reduce/filter_builder.rb +2 -2
  47. data/lib/riak/map_reduce/phase.rb +2 -2
  48. data/lib/riak/preflist_item.rb +7 -0
  49. data/lib/riak/rcontent.rb +8 -8
  50. data/lib/riak/robject.rb +27 -14
  51. data/lib/riak/search.rb +1 -0
  52. data/lib/riak/search/index.rb +17 -3
  53. data/lib/riak/search/query.rb +14 -6
  54. data/lib/riak/search/result_collection.rb +56 -3
  55. data/lib/riak/search/result_document.rb +71 -1
  56. data/lib/riak/search/schema.rb +6 -6
  57. data/lib/riak/secondary_index.rb +20 -12
  58. data/lib/riak/serializers.rb +0 -1
  59. data/lib/riak/util/escape.rb +2 -2
  60. data/lib/riak/util/translation.rb +1 -2
  61. data/lib/riak/version.rb +1 -1
  62. data/lib/riak/walk_spec.rb +67 -32
  63. data/riak-client.gemspec +5 -4
  64. data/spec/integration/riak/bucket_types_spec.rb +35 -5
  65. data/spec/integration/riak/conflict_resolution_spec.rb +1 -1
  66. data/spec/integration/riak/counters_spec.rb +1 -1
  67. data/spec/integration/riak/crdt/configuration_spec.rb +37 -0
  68. data/spec/integration/riak/crdt_search_spec.rb +176 -0
  69. data/spec/integration/riak/crdt_spec.rb +9 -33
  70. data/spec/integration/riak/crdt_validation/map_spec.rb +4 -4
  71. data/spec/integration/riak/crdt_validation/set_spec.rb +13 -13
  72. data/spec/integration/riak/preflist_spec.rb +31 -0
  73. data/spec/integration/riak/protobuffs/interrupted_request_spec.rb +2 -2
  74. data/spec/integration/riak/protobuffs_backends_spec.rb +9 -2
  75. data/spec/integration/riak/search_spec.rb +3 -3
  76. data/spec/integration/riak/secondary_index_spec.rb +3 -3
  77. data/spec/integration/riak/security_spec.rb +7 -7
  78. data/spec/integration/yokozuna/queries_spec.rb +1 -1
  79. data/spec/riak/beefcake_protobuffs_backend/bucket_properties_operator_spec.rb +9 -9
  80. data/spec/riak/beefcake_protobuffs_backend/crdt_operator_spec.rb +9 -9
  81. data/spec/riak/beefcake_protobuffs_backend/protocol_spec.rb +5 -5
  82. data/spec/riak/beefcake_protobuffs_backend_spec.rb +8 -8
  83. data/spec/riak/bucket_properties_spec.rb +27 -6
  84. data/spec/riak/bucket_spec.rb +5 -5
  85. data/spec/riak/bucket_type_spec.rb +21 -5
  86. data/spec/riak/bucket_typed/bucket_spec.rb +62 -0
  87. data/spec/riak/client_spec.rb +36 -18
  88. data/spec/riak/counter_spec.rb +4 -4
  89. data/spec/riak/crdt/counter_spec.rb +2 -2
  90. data/spec/riak/crdt/inner_flag_spec.rb +2 -2
  91. data/spec/riak/crdt/inner_map_spec.rb +4 -4
  92. data/spec/riak/crdt/inner_register_spec.rb +1 -1
  93. data/spec/riak/crdt/map_spec.rb +4 -4
  94. data/spec/riak/crdt/shared_examples.rb +5 -5
  95. data/spec/riak/crdt/typed_collection_spec.rb +21 -21
  96. data/spec/riak/map_reduce/filter_builder_spec.rb +2 -2
  97. data/spec/riak/map_reduce/phase_spec.rb +4 -4
  98. data/spec/riak/map_reduce_spec.rb +60 -42
  99. data/spec/riak/multiget_spec.rb +2 -2
  100. data/spec/riak/robject_spec.rb +55 -14
  101. data/spec/riak/search/index_spec.rb +12 -2
  102. data/spec/riak/search/query_spec.rb +4 -4
  103. data/spec/riak/search/result_collection_spec.rb +6 -4
  104. data/spec/riak/search/result_document_spec.rb +52 -9
  105. data/spec/riak/search/schema_spec.rb +2 -2
  106. data/spec/riak/secondary_index_spec.rb +6 -6
  107. data/spec/riak/serializers_spec.rb +27 -10
  108. data/spec/riak/walk_spec_spec.rb +10 -6
  109. data/spec/spec_helper.rb +11 -2
  110. data/spec/support/crdt_search_config.rb +112 -0
  111. data/spec/support/crdt_search_fixtures.rb +42 -0
  112. data/spec/support/search_config.rb +7 -5
  113. data/spec/support/search_corpus_setup.rb +2 -2
  114. data/spec/support/test_client.rb +2 -2
  115. data/spec/support/unified_backend_examples.rb +5 -5
  116. data/spec/support/version_filter.rb +5 -3
  117. data/spec/support/wait_until.rb +9 -3
  118. metadata +36 -13
  119. data/spec/riak/bucket_typed/bucket.rb +0 -43
@@ -8,7 +8,7 @@ module Riak::Search
8
8
  class ResultCollection
9
9
  # @return [Riak::Client]
10
10
  attr_reader :client
11
-
11
+
12
12
  # @return [Hash] the de-serialzed hash returned from Solr
13
13
  attr_reader :raw
14
14
 
@@ -52,13 +52,13 @@ module Riak::Search
52
52
  end
53
53
 
54
54
  # @param [Integer] index the index of the [Riak::RObject] to load and return
55
- # @return [Riak::RObject,NilClass] the found object, or nil if the index
55
+ # @return [Riak::RObject,NilClass] the found object, or nil if the index
56
56
  # is out of range
57
57
  def [](index)
58
58
  doc = docs[index]
59
59
  return nil if doc.nil?
60
60
 
61
- doc.robject
61
+ doc.object
62
62
  end
63
63
 
64
64
  # @return [Riak::RObject,NilClass] the first found object, or nil if the
@@ -67,6 +67,59 @@ module Riak::Search
67
67
  self[0]
68
68
  end
69
69
 
70
+ # Materializes and returns an array of objects from search results.
71
+ # You'll probably need to type inspect its members.
72
+ #
73
+ # @return [Array] materialized objects
74
+ def objects
75
+ @objects ||= docs.map do |doc|
76
+ next doc.crdt if doc.crdt?
77
+ doc.robject
78
+ end
79
+ end
80
+
81
+ # Materializes [Riak::RObject]s from any key-value results. Refuses to
82
+ # return RObjects for any CRDT results.
83
+ #
84
+ # @return [Array<Riak::RObject>] key-value objects
85
+ def robjects
86
+ @robjects ||= docs.reject(&:crdt?).map(&:robject)
87
+ end
88
+
89
+ # Materializes [Riak::Crdt::Base] subclasses from any CRDT results.
90
+ #
91
+ # @return [Array<Riak::Crdt::Base>] CRDT objects
92
+ def crdts
93
+ @crdts ||= docs.select(&:crdt?).map(&:crdt)
94
+ end
95
+
96
+ # Materializes [Riak::Crdt::Counter] results.
97
+ #
98
+ # @return [Array<Riak::Crdt::Counter] counter objects
99
+ def counters
100
+ @counters ||= docs.
101
+ select{ |d| d.type_class == Riak::Crdt::Counter }.
102
+ map(&:counter)
103
+ end
104
+
105
+ # Materializes [Riak::Crdt::Map] results.
106
+ #
107
+ # @return [Array<Riak::Crdt::Map] map objects
108
+ def maps
109
+ @maps ||= docs.
110
+ select{ |d| d.type_class == Riak::Crdt::Map }.
111
+ map(&:map)
112
+ end
113
+
114
+ # Materializes [Riak::Crdt::Set] results.
115
+ #
116
+ # @return [Array<Riak::Crdt::Set>]
117
+ def sets
118
+ @sets ||= docs.
119
+ select{ |d| d.type_class == Riak::Crdt::Set }.
120
+ map(&:set)
121
+ end
122
+
70
123
  # {Enumerable}-compatible iterator method. If a block is given, yields with
71
124
  # each {Riak::RObject} in the collection. If no block is given, returns an
72
125
  # {Enumerator} over each {Riak::RObject in the collection.
@@ -1,7 +1,9 @@
1
+ require 'riak/errors/crdt_error'
2
+
1
3
  module Riak::Search
2
4
 
3
5
  # A single document from a Riak Search 2 response. Materializes the document
4
- # fields into {Riak::BucketType}, {Riak::Bucket}, and {Riak::RObject}
6
+ # fields into {Riak::BucketType}, {Riak::Bucket}, and {Riak::RObject}
5
7
  # instances on demand.
6
8
  class ResultDocument
7
9
  # @return [Riak::Client]
@@ -41,6 +43,55 @@ module Riak::Search
41
43
  @score ||= Float(raw['score'])
42
44
  end
43
45
 
46
+ # Determining if the object is a CRDT or regular K-V object requires
47
+ # figuring out what data type the bucket type contains. If the bucket type
48
+ # has no data type, treat it as a regular K-V object.
49
+ #
50
+ # @return [Class] the class of the object referred to by the search result
51
+ def type_class
52
+ bucket_type.data_type_class || Riak::RObject
53
+ end
54
+
55
+ # @return [Boolean] if the object is a CRDT
56
+ def crdt?
57
+ type_class != Riak::RObject
58
+ end
59
+
60
+ # @raise [Riak::CrdtError::NotACrdt] if the result is not a CRDT
61
+ # @return [Riak::Crdt::Base] the materialized CRDT
62
+ def crdt
63
+ fail Riak::CrdtError::NotACrdt unless crdt?
64
+
65
+ type_class.new bucket, key, bucket_type
66
+ end
67
+
68
+ # If the result document describes a counter, return it.
69
+ #
70
+ # @return [Riak::Crdt::Counter]
71
+ # @raise [Riak::CrdtError::NotACrdt] if the result is not a CRDT
72
+ # @raise [Riak::CrdtError::UnexpectedDataType] if the CRDT is not a counter
73
+ def counter
74
+ return crdt if check_type_class Riak::Crdt::Counter
75
+ end
76
+
77
+ # If the result document describes a map, return it.
78
+ #
79
+ # @return [Riak::Crdt::Map]
80
+ # @raise [Riak::CrdtError::NotACrdt] if the result is not a CRDT
81
+ # @raise [Riak::CrdtError::UnexpectedDataType] if the CRDT is not a map
82
+ def map
83
+ return crdt if check_type_class Riak::Crdt::Map
84
+ end
85
+
86
+ # If the result document describes a set, return it.
87
+ #
88
+ # @return [Riak::Crdt::Set]
89
+ # @raise [Riak::CrdtError::NotACrdt] if the result is not a CRDT
90
+ # @raise [Riak::CrdtError::UnexpectedDataType] if the CRDT is not a set
91
+ def set
92
+ return crdt if check_type_class Riak::Crdt::Set
93
+ end
94
+
44
95
  # Provides access to other parts of the result document without
45
96
  # materializing them. Useful when querying non-default fields.
46
97
  #
@@ -53,7 +104,26 @@ module Riak::Search
53
104
  #
54
105
  # @return [Riak::RObject]
55
106
  def robject
107
+ if crdt?
108
+ fail Riak::SearchError::UnexpectedResultError.
109
+ new(Riak::RObject, type_class)
110
+ end
111
+
56
112
  @robject ||= bucket.get key
57
113
  end
114
+
115
+ # Returns an appropriate object, be it CRDT or K-V.
116
+ def object
117
+ return crdt if crdt?
118
+ robject
119
+ end
120
+
121
+ private
122
+
123
+ def check_type_class(klass)
124
+ return true if type_class == klass
125
+ fail Riak::CrdtError::NotACrdt if type_class == Riak::RObject
126
+ fail Riak::CrdtError::UnexpectedDataType.new(klass, type_class)
127
+ end
58
128
  end
59
129
  end
@@ -27,23 +27,24 @@ module Riak::Search
27
27
  def content
28
28
  schema_data.content
29
29
  end
30
-
30
+
31
31
  # @param [String] content the XML content of this schema
32
32
  # @raise [Riak::SearchError::SchemaExistsError] if a schema with the given
33
33
  # name already exists
34
34
  def create!(content)
35
- raise Riak::SearchError::SchemaExistsError.new name if exists?
35
+ fail Riak::SearchError::SchemaExistsError.new name if exists?
36
36
 
37
37
  @client.backend do |b|
38
38
  b.create_search_schema name, content
39
39
  end
40
-
40
+
41
41
  @schema_data = nil
42
42
 
43
43
  true
44
44
  end
45
45
 
46
46
  private
47
+
47
48
  def schema_data
48
49
  return @schema_data if defined?(@schema_data) && @schema_data
49
50
 
@@ -57,9 +58,8 @@ module Riak::Search
57
58
  return nil if e.not_found?
58
59
  raise e
59
60
  end
60
-
61
- return @schema_data = sd
61
+
62
+ @schema_data = sd
62
63
  end
63
64
  end
64
65
  end
65
-
@@ -2,6 +2,9 @@ require 'riak/index_collection'
2
2
  require 'riak/bucket_typed/bucket'
3
3
 
4
4
  module Riak
5
+ # {Riak::SecondaryIndex} provides an object-oriented interface to secondary
6
+ # index ("2i") functionality in Riak, available on the `memory` and `leveldb`
7
+ # backends.
5
8
  class SecondaryIndex
6
9
  include Util::Translation
7
10
  include Client::FeatureDetection
@@ -11,7 +14,7 @@ module Riak
11
14
  # @param [String] index the index name
12
15
  # @param [String,Integer,Range<String,Integer>] query
13
16
  # a single value or range of values to query for
14
- def initialize(bucket, index, query, options={})
17
+ def initialize(bucket, index, query, options = {})
15
18
  @bucket = bucket
16
19
  @client = @bucket.client
17
20
  @index = index
@@ -26,7 +29,7 @@ module Riak
26
29
  end
27
30
 
28
31
  def get_server_version
29
- @client.backend{|b| b.send :get_server_version }
32
+ @client.backend { |b| b.send :get_server_version }
30
33
  end
31
34
 
32
35
  # Get the array of matched keys
@@ -39,17 +42,17 @@ module Riak
39
42
 
40
43
  # Get the array of values
41
44
  def values
42
- @values ||= @bucket.get_many(self.keys).values
45
+ @values ||= @bucket.get_many(keys).values
43
46
  end
44
47
 
45
48
  # Get a new SecondaryIndex fetch for the next page
46
49
  def next_page
47
- raise t('index.no_next_page') unless keys.continuation
50
+ fail t('index.no_next_page') unless keys.continuation
48
51
 
49
- self.class.new(@bucket,
50
- @index,
51
- @query,
52
- @options.merge(:continuation => keys.continuation))
52
+ self.class.new(@bucket,
53
+ @index,
54
+ @query,
55
+ @options.merge(continuation: keys.continuation))
53
56
  end
54
57
 
55
58
  # Determine whether a SecondaryIndex fetch has a next page available
@@ -58,12 +61,17 @@ module Riak
58
61
  end
59
62
 
60
63
  private
64
+
61
65
  def validate_options
62
- raise t('index.pagination_not_available') if paginated? && !index_pagination?
63
- raise t('index.return_terms_not_available') if @options[:return_terms] && !index_return_terms?
64
- raise t('index.include_terms_is_wrong') if @options[:include_terms]
66
+ if paginated? && !index_pagination?
67
+ fail t('index.pagination_not_available')
68
+ end
69
+
70
+ if @options[:return_terms] && !index_return_terms?
71
+ fail t('index.return_terms_not_available')
72
+ end
65
73
 
66
- # raise t('index.streaming_not_available') if @options[:stream] && !index_streaming?
74
+ fail t('index.include_terms_is_wrong') if @options[:include_terms]
67
75
  end
68
76
 
69
77
  def paginated?
@@ -71,4 +71,3 @@ module Riak
71
71
  end
72
72
  end
73
73
  end
74
-
@@ -52,9 +52,9 @@ module Riak
52
52
  # @return [String] the escaped path segment
53
53
  def escape(bucket_or_key)
54
54
  if Riak.escaper == URI
55
- Riak.escaper.escape(bucket_or_key.to_s).gsub(" ", "%20").gsub("+", "%2B").gsub('/', "%2F")
55
+ Riak.escaper.escape(bucket_or_key.to_s).gsub(/[ \+\/]/, { " " => "%20", "+" => "%2B", "/" => "%2F"})
56
56
  else #CGI
57
- Riak.escaper.escape(bucket_or_key.to_s).gsub("+", "%20").gsub('/', "%2F")
57
+ Riak.escaper.escape(bucket_or_key.to_s).gsub(/[\+\/]/, { "+" => "%20", "/" => "%2F"})
58
58
  end
59
59
  end
60
60
 
@@ -10,10 +10,9 @@ module Riak
10
10
  end
11
11
 
12
12
  # Provides the translation for a given internationalized message
13
- def t(message, options={})
13
+ def t(message, options = {})
14
14
  I18n.t("#{i18n_scope}.#{message}", options)
15
15
  end
16
16
  end
17
17
  end
18
18
  end
19
-
data/lib/riak/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Riak
2
- VERSION = "2.2.0.pre1"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -19,13 +19,16 @@ module Riak
19
19
  extend Util::Translation
20
20
  include Util::Escape
21
21
 
22
- # @return [String] The bucket followed links should be restricted to. "_" represents all buckets.
22
+ # @return [String] The bucket followed links should be restricted to.
23
+ # "_" represents all buckets.
23
24
  attr_accessor :bucket
24
25
 
25
- # @return [String] The "riaktag" or "rel" that followed links should be restricted to. "_" represents all tags.
26
+ # @return [String] The "riaktag" or "rel" that followed links should be
27
+ # restricted to. "_" represents all tags.
26
28
  attr_accessor :tag
27
29
 
28
- # @return [Boolean] Whether objects should be returned from this phase of link walking. Default is false.
30
+ # @return [Boolean] Whether objects should be returned from this phase
31
+ # of link walking. Default is false.
29
32
  attr_accessor :keep
30
33
 
31
34
  # Normalize a list of walk specs into WalkSpec objects.
@@ -33,18 +36,13 @@ module Riak
33
36
  params.flatten!
34
37
  specs = []
35
38
  while params.length > 0
36
- param = params.shift
37
- case param
39
+ case param = params.shift
38
40
  when Hash
39
41
  specs << new(param)
40
42
  when WalkSpec
41
43
  specs << param
42
44
  else
43
- if params.length >= 2
44
- specs << new(param, params.shift, params.shift)
45
- else
46
- raise ArgumentError, t("too_few_arguments", :params => params.inspect)
47
- end
45
+ normalize_long_params specs, params, param
48
46
  end
49
47
  end
50
48
  specs
@@ -54,30 +52,34 @@ module Riak
54
52
  # @overload initialize(hash)
55
53
  # Creates a walk-spec from a hash.
56
54
  # @param [Hash] hash options for the walk-spec
57
- # @option hash [String] :bucket ("_") the bucket the links should point to (default '_' is all)
58
- # @option hash [String] :tag ("_") the tag to filter links by (default '_' is all)
59
- # @option hash [Boolean] :keep (false) whether to return results from following this link specification
55
+ # @option hash [String] :bucket ("_") the bucket the links should point to
56
+ # (default '_' is all)
57
+ # @option hash [String] :tag ("_") the tag to filter links by (default '_'
58
+ # is all)
59
+ # @option hash [Boolean] :keep (false) whether to return results from
60
+ # following this link specification
60
61
  # @overload initialize(bucket, tag, keep)
61
62
  # Creates a walk-spec from a bucket-tag-result triple.
62
- # @param [String] bucket the bucket the links should point to (default '_' is all)
63
+ # @param [String] bucket the bucket the links should point to (default '_'
64
+ # is all)
63
65
  # @param [String] tag the tag to filter links by (default '_' is all)
64
- # @param [Boolean] keep whether to return results from following this link specification
66
+ # @param [Boolean] keep whether to return results from following this link
67
+ # specification
65
68
  # @see {Riak::RObject#walk}
66
69
  def initialize(*args)
67
70
  args.flatten!
68
71
  case args.size
69
72
  when 1
70
- hash = args.first
71
- raise ArgumentError, t("hash_type", :hash => hash.inspect) unless Hash === hash
72
- assign(hash[:bucket], hash[:tag], hash[:keep])
73
+ assign_from_hash args.first
73
74
  when 3
74
75
  assign(*args)
75
76
  else
76
- raise ArgumentError, t("wrong_argument_count_walk_spec")
77
+ fail ArgumentError, t('wrong_argument_count_walk_spec')
77
78
  end
78
79
  end
79
80
 
80
- # Converts the walk-spec into the form required by the link-walker resource URL
81
+ # Converts the walk-spec into the form required by the link-walker resource
82
+ # URL
81
83
  def to_s
82
84
  b = @bucket && escape(@bucket) || '_'
83
85
  t = @tag && escape(@tag) || '_'
@@ -85,26 +87,59 @@ module Riak
85
87
  end
86
88
 
87
89
  def ==(other)
88
- other.is_a?(WalkSpec) && other.bucket == bucket && other.tag == tag && other.keep == keep
90
+ return false unless other.is_a? WalkSpec
91
+ return false unless other.bucket == bucket
92
+ return false unless other.tag == tag
93
+ return false unless other.keep == keep
94
+ true
89
95
  end
90
96
 
91
97
  def ===(other)
92
- self == other || case other
93
- when WalkSpec
94
- other.keep == keep &&
95
- (bucket == "_" || bucket == other.bucket) &&
96
- (tag == "_" || tag == other.tag)
97
- when Link
98
- (bucket == "_" || bucket == other.url.split("/")[2]) &&
99
- (tag == "_" || tag == other.rel)
100
- end
98
+ return true if self == other
99
+ case other
100
+ when WalkSpec
101
+ walkspec_threequality(other)
102
+ when Link
103
+ link_threequality(other)
104
+ end
101
105
  end
102
106
 
103
107
  private
108
+
109
+ def self.normalize_long_params(specs, params, param)
110
+ if params.length >= 2
111
+ specs << new(param, params.shift, params.shift)
112
+ else
113
+ fail ArgumentError, t('too_few_arguments',
114
+ params: params.inspect)
115
+ end
116
+ end
117
+
118
+ def assign_from_hash(hash)
119
+ unless hash.is_a? Hash
120
+ fail ArgumentError, t('hash_type', hash: hash.inspect)
121
+ end
122
+
123
+ assign(hash[:bucket], hash[:tag], hash[:keep])
124
+ end
125
+
104
126
  def assign(bucket, tag, result)
105
- @bucket = bucket || "_"
106
- @tag = tag || "_"
127
+ @bucket = bucket || '_'
128
+ @tag = tag || '_'
107
129
  @keep = result || false
108
130
  end
131
+
132
+ def walkspec_threequality(other)
133
+ return false unless other.keep == keep
134
+ return false unless bucket == '_' || bucket == other.bucket
135
+ return false unless tag == '_' || tag == other.tag
136
+ true
137
+ end
138
+
139
+ def link_threequality(other)
140
+ return false unless bucket == '_' || bucket == other.url.split('/')[2]
141
+ return false unless tag == '_' || tag == other.rel
142
+ true
143
+ end
109
144
  end
110
145
  end