bindata 0.9.3 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bindata might be problematic. Click here for more details.
- data/ChangeLog +18 -0
- data/NEWS +59 -0
- data/README +22 -23
- data/TODO +18 -12
- data/examples/gzip.rb +4 -4
- data/lib/bindata.rb +4 -3
- data/lib/bindata/array.rb +202 -132
- data/lib/bindata/base.rb +147 -166
- data/lib/bindata/{single.rb → base_primitive.rb} +82 -56
- data/lib/bindata/bits.rb +31 -770
- data/lib/bindata/choice.rb +157 -82
- data/lib/bindata/float.rb +25 -27
- data/lib/bindata/int.rb +144 -177
- data/lib/bindata/io.rb +59 -49
- data/lib/bindata/lazy.rb +80 -50
- data/lib/bindata/params.rb +134 -26
- data/lib/bindata/{single_value.rb → primitive.rb} +71 -64
- data/lib/bindata/{multi_value.rb → record.rb} +52 -70
- data/lib/bindata/registry.rb +49 -17
- data/lib/bindata/rest.rb +6 -10
- data/lib/bindata/sanitize.rb +55 -70
- data/lib/bindata/string.rb +60 -42
- data/lib/bindata/stringz.rb +34 -35
- data/lib/bindata/struct.rb +197 -152
- data/lib/bindata/trace.rb +35 -0
- data/spec/array_spec.rb +128 -112
- data/spec/{single_spec.rb → base_primitive_spec.rb} +102 -61
- data/spec/base_spec.rb +190 -185
- data/spec/bits_spec.rb +126 -98
- data/spec/choice_spec.rb +89 -98
- data/spec/example.rb +19 -0
- data/spec/float_spec.rb +28 -44
- data/spec/int_spec.rb +217 -127
- data/spec/io_spec.rb +41 -24
- data/spec/lazy_spec.rb +95 -49
- data/spec/primitive_spec.rb +191 -0
- data/spec/{multi_value_spec.rb → record_spec.rb} +124 -89
- data/spec/registry_spec.rb +53 -12
- data/spec/rest_spec.rb +2 -3
- data/spec/sanitize_spec.rb +47 -73
- data/spec/spec_common.rb +13 -1
- data/spec/string_spec.rb +34 -23
- data/spec/stringz_spec.rb +10 -18
- data/spec/struct_spec.rb +91 -63
- data/spec/system_spec.rb +291 -0
- metadata +12 -8
- data/spec/single_value_spec.rb +0 -131
data/lib/bindata/io.rb
CHANGED
@@ -53,7 +53,6 @@ module BinData
|
|
53
53
|
if positioning_supported?
|
54
54
|
@raw_io.pos - @initial_pos
|
55
55
|
else
|
56
|
-
# stream does not support positioning
|
57
56
|
0
|
58
57
|
end
|
59
58
|
end
|
@@ -69,7 +68,7 @@ module BinData
|
|
69
68
|
#
|
70
69
|
# If the data read is too short an IOError is raised.
|
71
70
|
def readbytes(n)
|
72
|
-
raise "Internal state error nbits = #{@rnbits}" if @rnbits
|
71
|
+
raise "Internal state error nbits = #{@rnbits}" if @rnbits >= 8
|
73
72
|
@rnbits = 0
|
74
73
|
@rval = 0
|
75
74
|
|
@@ -79,9 +78,18 @@ module BinData
|
|
79
78
|
str
|
80
79
|
end
|
81
80
|
|
81
|
+
# Reads all remaining bytes from the stream.
|
82
|
+
def read_all_bytes
|
83
|
+
raise "Internal state error nbits = #{@rnbits}" if @rnbits >= 8
|
84
|
+
@rnbits = 0
|
85
|
+
@rval = 0
|
86
|
+
|
87
|
+
@raw_io.read
|
88
|
+
end
|
89
|
+
|
82
90
|
# Reads exactly +nbits+ bits from the stream. +endian+ specifies whether
|
83
91
|
# the bits are stored in +:big+ or +:little+ endian format.
|
84
|
-
def readbits(nbits, endian
|
92
|
+
def readbits(nbits, endian)
|
85
93
|
if @rendian != endian
|
86
94
|
# don't mix bits of differing endian
|
87
95
|
@rnbits = 0
|
@@ -90,9 +98,9 @@ module BinData
|
|
90
98
|
end
|
91
99
|
|
92
100
|
if endian == :big
|
93
|
-
|
101
|
+
read_big_endian_bits(nbits)
|
94
102
|
else
|
95
|
-
|
103
|
+
read_little_endian_bits(nbits)
|
96
104
|
end
|
97
105
|
end
|
98
106
|
|
@@ -104,32 +112,29 @@ module BinData
|
|
104
112
|
|
105
113
|
# Writes +nbits+ bits from +val+ to the stream. +endian+ specifies whether
|
106
114
|
# the bits are to be stored in +:big+ or +:little+ endian format.
|
107
|
-
def writebits(val, nbits, endian
|
108
|
-
# clamp val to range
|
109
|
-
val = val & ((1 << nbits) - 1)
|
110
|
-
|
115
|
+
def writebits(val, nbits, endian)
|
111
116
|
if @wendian != endian
|
112
117
|
# don't mix bits of differing endian
|
113
|
-
flushbits
|
118
|
+
flushbits
|
114
119
|
|
115
120
|
@wendian = endian
|
116
121
|
end
|
117
122
|
|
123
|
+
clamped_val = val & mask(nbits)
|
124
|
+
|
118
125
|
if endian == :big
|
119
|
-
|
126
|
+
write_big_endian_bits(clamped_val, nbits)
|
120
127
|
else
|
121
|
-
|
128
|
+
write_little_endian_bits(clamped_val, nbits)
|
122
129
|
end
|
123
130
|
end
|
124
131
|
|
125
132
|
# To be called after all +writebits+ have been applied.
|
126
133
|
def flushbits
|
127
|
-
if @wnbits
|
128
|
-
|
129
|
-
|
134
|
+
raise "Internal state error nbits = #{@wnbits}" if @wnbits >= 8
|
135
|
+
|
136
|
+
if @wnbits > 0
|
130
137
|
writebits(0, 8 - @wnbits, @wendian)
|
131
|
-
else
|
132
|
-
# do nothing
|
133
138
|
end
|
134
139
|
end
|
135
140
|
alias_method :flush, :flushbits
|
@@ -137,7 +142,6 @@ module BinData
|
|
137
142
|
#---------------
|
138
143
|
private
|
139
144
|
|
140
|
-
# Checks if positioning is supported by the underlying io stream.
|
141
145
|
def positioning_supported?
|
142
146
|
unless defined? @positioning_supported
|
143
147
|
@positioning_supported = begin
|
@@ -150,52 +154,55 @@ module BinData
|
|
150
154
|
@positioning_supported
|
151
155
|
end
|
152
156
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
while nbits > @rnbits
|
157
|
-
byte = @raw_io.read(1)
|
158
|
-
raise EOFError, "End of file reached" if byte.nil?
|
159
|
-
byte = byte.unpack('C').at(0) & 0xff
|
160
|
-
|
161
|
-
@rval = (@rval << 8) | byte
|
162
|
-
@rnbits += 8
|
157
|
+
def read_big_endian_bits(nbits)
|
158
|
+
while @rnbits < nbits
|
159
|
+
accumulate_big_endian_bits
|
163
160
|
end
|
164
161
|
|
165
|
-
val = (@rval >> (@rnbits - nbits)) & (
|
162
|
+
val = (@rval >> (@rnbits - nbits)) & mask(nbits)
|
166
163
|
@rnbits -= nbits
|
167
|
-
@rval &= (
|
164
|
+
@rval &= mask(@rnbits)
|
168
165
|
|
169
166
|
val
|
170
167
|
end
|
171
168
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
169
|
+
def accumulate_big_endian_bits
|
170
|
+
byte = @raw_io.read(1)
|
171
|
+
raise EOFError, "End of file reached" if byte.nil?
|
172
|
+
byte = byte.unpack('C').at(0) & 0xff
|
173
|
+
|
174
|
+
@rval = (@rval << 8) | byte
|
175
|
+
@rnbits += 8
|
176
|
+
end
|
179
177
|
|
180
|
-
|
181
|
-
|
178
|
+
def read_little_endian_bits(nbits)
|
179
|
+
while @rnbits < nbits
|
180
|
+
accumulate_little_endian_bits
|
182
181
|
end
|
183
182
|
|
184
|
-
val = @rval & (
|
183
|
+
val = @rval & mask(nbits)
|
185
184
|
@rnbits -= nbits
|
186
185
|
@rval >>= nbits
|
187
186
|
|
188
187
|
val
|
189
188
|
end
|
190
189
|
|
191
|
-
|
192
|
-
|
190
|
+
def accumulate_little_endian_bits
|
191
|
+
byte = @raw_io.read(1)
|
192
|
+
raise EOFError, "End of file reached" if byte.nil?
|
193
|
+
byte = byte.unpack('C').at(0) & 0xff
|
194
|
+
|
195
|
+
@rval = @rval | (byte << @rnbits)
|
196
|
+
@rnbits += 8
|
197
|
+
end
|
198
|
+
|
199
|
+
def write_big_endian_bits(val, nbits)
|
193
200
|
while nbits > 0
|
194
201
|
bits_req = 8 - @wnbits
|
195
202
|
if nbits >= bits_req
|
196
|
-
msb_bits = (val >> (nbits - bits_req)) & (
|
203
|
+
msb_bits = (val >> (nbits - bits_req)) & mask(bits_req)
|
197
204
|
nbits -= bits_req
|
198
|
-
val &= (
|
205
|
+
val &= mask(nbits)
|
199
206
|
|
200
207
|
@wval = (@wval << bits_req) | msb_bits
|
201
208
|
@raw_io.write(@wval.chr)
|
@@ -210,26 +217,29 @@ module BinData
|
|
210
217
|
end
|
211
218
|
end
|
212
219
|
|
213
|
-
|
214
|
-
def write_le_bits(val, nbits)
|
220
|
+
def write_little_endian_bits(val, nbits)
|
215
221
|
while nbits > 0
|
216
222
|
bits_req = 8 - @wnbits
|
217
223
|
if nbits >= bits_req
|
218
|
-
lsb_bits = val & (
|
224
|
+
lsb_bits = val & mask(bits_req)
|
219
225
|
nbits -= bits_req
|
220
226
|
val >>= bits_req
|
221
227
|
|
222
|
-
@wval
|
228
|
+
@wval = @wval | (lsb_bits << @wnbits)
|
223
229
|
@raw_io.write(@wval.chr)
|
224
230
|
|
225
231
|
@wval = 0
|
226
232
|
@wnbits = 0
|
227
233
|
else
|
228
|
-
@wval
|
234
|
+
@wval = @wval | (val << @wnbits)
|
229
235
|
@wnbits += nbits
|
230
236
|
nbits = 0
|
231
237
|
end
|
232
238
|
end
|
233
239
|
end
|
240
|
+
|
241
|
+
def mask(nbits)
|
242
|
+
(1 << nbits) - 1
|
243
|
+
end
|
234
244
|
end
|
235
245
|
end
|
data/lib/bindata/lazy.rb
CHANGED
@@ -5,31 +5,56 @@ module BinData
|
|
5
5
|
#
|
6
6
|
# BinData::String.new(:value => lambda { %w{a test message}.join(" ") })
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
8
|
+
# As a shortcut, :foo is the equivalent of lambda { foo }.
|
9
|
+
#
|
10
|
+
# When evaluating lambdas, unknown methods are resolved in the context of the
|
11
|
+
# parent of the bound data object. Resolution is attempted firstly as keys
|
12
|
+
# in #parameters, and secondly as methods in this parent. This
|
13
|
+
# resolution propagates up the chain of parent data objects.
|
14
|
+
#
|
15
|
+
# An evaluation will recurse until it returns a result that is not
|
16
|
+
# a lambda or a symbol.
|
12
17
|
#
|
13
18
|
# This resolution process makes the lambda easier to read as we just write
|
14
19
|
# <tt>field</tt> instead of <tt>obj.field</tt>.
|
15
20
|
class LazyEvaluator
|
21
|
+
|
16
22
|
class << self
|
17
23
|
# Lazily evaluates +val+ in the context of +obj+, with possibility of
|
18
24
|
# +overrides+.
|
19
|
-
def eval(
|
20
|
-
|
21
|
-
|
25
|
+
def eval(obj, val, overrides = {})
|
26
|
+
if can_eval?(val)
|
27
|
+
env = self.new(obj, overrides)
|
28
|
+
env.lazy_eval(val)
|
29
|
+
else
|
30
|
+
val
|
31
|
+
end
|
22
32
|
end
|
23
|
-
end
|
24
33
|
|
25
|
-
|
26
|
-
|
34
|
+
#-------------
|
35
|
+
private
|
36
|
+
|
37
|
+
def can_eval?(val)
|
38
|
+
val.is_a?(Symbol) or val.respond_to?(:arity)
|
39
|
+
end
|
40
|
+
end
|
27
41
|
|
28
42
|
# Creates a new evaluator. All lazy evaluation is performed in the
|
29
43
|
# context of +obj+.
|
30
|
-
|
44
|
+
# +overrides+ is an optional +obj.parameters+ like hash.
|
45
|
+
def initialize(obj, overrides = {})
|
31
46
|
@obj = obj
|
32
|
-
@overrides =
|
47
|
+
@overrides = overrides
|
48
|
+
end
|
49
|
+
|
50
|
+
def lazy_eval(val)
|
51
|
+
if val.is_a? Symbol
|
52
|
+
__send__(val)
|
53
|
+
elsif val.respond_to? :arity
|
54
|
+
instance_eval(&val)
|
55
|
+
else
|
56
|
+
val
|
57
|
+
end
|
33
58
|
end
|
34
59
|
|
35
60
|
# Returns a LazyEvaluator for the parent of this data object.
|
@@ -41,35 +66,30 @@ module BinData
|
|
41
66
|
end
|
42
67
|
end
|
43
68
|
|
44
|
-
#
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
69
|
+
# Returns the index of this data object inside it's nearest container
|
70
|
+
# array.
|
71
|
+
def index
|
72
|
+
return @overrides[:index] if @overrides.has_key?(:index)
|
73
|
+
|
74
|
+
bindata_array_class = BinData.const_defined?("Array") ?
|
75
|
+
BinData.const_get("Array") : nil
|
76
|
+
child = @obj
|
77
|
+
parent = @obj.parent
|
78
|
+
while parent
|
79
|
+
if parent.class == bindata_array_class
|
80
|
+
return parent.find_index_of(child)
|
81
|
+
end
|
82
|
+
child = parent
|
83
|
+
parent = parent.parent
|
55
84
|
end
|
56
|
-
|
57
|
-
result
|
85
|
+
raise NoMethodError, "no index found"
|
58
86
|
end
|
59
87
|
|
60
88
|
def method_missing(symbol, *args)
|
61
|
-
if @overrides.
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
elsif @obj.parent
|
66
|
-
val = symbol
|
67
|
-
if @obj.parent.parameters and @obj.parent.parameters.has_key?(symbol)
|
68
|
-
val = @obj.parent.parameters[symbol]
|
69
|
-
elsif @obj.parent and @obj.parent.respond_to?(symbol)
|
70
|
-
val = @obj.parent.__send__(symbol, *args)
|
71
|
-
end
|
72
|
-
LazyEvaluator.eval(val, @obj.parent)
|
89
|
+
return @overrides[symbol] if @overrides.has_key?(symbol)
|
90
|
+
|
91
|
+
if @obj.parent
|
92
|
+
eval_symbol_in_parent_context(symbol, args)
|
73
93
|
else
|
74
94
|
super
|
75
95
|
end
|
@@ -78,21 +98,31 @@ module BinData
|
|
78
98
|
#---------------
|
79
99
|
private
|
80
100
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
101
|
+
def eval_symbol_in_parent_context(symbol, args)
|
102
|
+
result = resolve_symbol_in_parent_context(symbol, args)
|
103
|
+
recursively_eval(result, args)
|
104
|
+
end
|
105
|
+
|
106
|
+
def resolve_symbol_in_parent_context(symbol, args)
|
107
|
+
obj_parent = @obj.parent
|
108
|
+
|
109
|
+
if obj_parent.has_parameter?(symbol)
|
110
|
+
result = obj_parent.get_parameter(symbol)
|
111
|
+
elsif obj_parent.respond_to?(symbol)
|
112
|
+
result = obj_parent.__send__(symbol, *args)
|
113
|
+
else
|
114
|
+
result = symbol
|
93
115
|
end
|
94
|
-
raise NoMethodError, "no index found"
|
95
116
|
end
|
96
117
|
|
118
|
+
def recursively_eval(val, args)
|
119
|
+
if val.is_a?(Symbol)
|
120
|
+
parent.__send__(val, *args)
|
121
|
+
elsif val.respond_to?(:arity)
|
122
|
+
parent.instance_eval(&val)
|
123
|
+
else
|
124
|
+
val
|
125
|
+
end
|
126
|
+
end
|
97
127
|
end
|
98
128
|
end
|
data/lib/bindata/params.rb
CHANGED
@@ -1,36 +1,144 @@
|
|
1
1
|
module BinData
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
sym = full_name_plural.to_sym
|
13
|
-
iv = "@#{full_name_plural}".to_sym
|
14
|
-
|
15
|
-
body = Proc.new do |*args|
|
16
|
-
# initialize collection to duplicate ancestor's collection
|
17
|
-
unless instance_variable_defined?(iv)
|
18
|
-
ancestor = ancestors[1..-1].find { |a| a.respond_to?(sym) }
|
19
|
-
val = ancestor.nil? ? empty : ancestor.send(sym).dup
|
20
|
-
instance_variable_set(iv, val)
|
2
|
+
# BinData objects accept parameters when initializing. AcceptedParameters
|
3
|
+
# allow a BinData class to declaratively identify accepted parameters as
|
4
|
+
# mandatory, optional, default or mutually exclusive.
|
5
|
+
class AcceptedParameters
|
6
|
+
class << self
|
7
|
+
def define_all_accessors(obj_class, param_name)
|
8
|
+
all_accessors = [:mandatory, :optional, :default, :mutually_exclusive]
|
9
|
+
all_accessors.each do |accessor|
|
10
|
+
define_accessor(obj_class, param_name, accessor)
|
21
11
|
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def define_accessors(obj_class, param_name, *accessors)
|
15
|
+
accessors.each do |accessor|
|
16
|
+
define_accessor(obj_class, param_name, accessor)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def get(obj_class, param_name)
|
21
|
+
obj_class.__send__(internal_storage_method_name(param_name))
|
22
|
+
end
|
23
|
+
|
24
|
+
#-------------
|
25
|
+
private
|
26
|
+
|
27
|
+
def define_accessor(obj_class, param_name, accessor)
|
28
|
+
singular_name = accessor_method_name(accessor)
|
29
|
+
plural_name = singular_name + "s"
|
30
|
+
internal_storage_method = internal_storage_method_name(param_name)
|
31
|
+
|
32
|
+
ensure_parameter_storage_exists(obj_class, internal_storage_method)
|
33
|
+
|
34
|
+
obj_class.class_eval <<-END
|
35
|
+
def #{singular_name}(*args)
|
36
|
+
#{internal_storage_method}.#{accessor}(*args)
|
37
|
+
end
|
38
|
+
alias_method :#{plural_name}, :#{singular_name}
|
39
|
+
END
|
40
|
+
end
|
41
|
+
|
42
|
+
def accessor_method_name(accessor)
|
43
|
+
"#{accessor}_parameter"
|
44
|
+
end
|
45
|
+
|
46
|
+
def internal_storage_method_name(param_name)
|
47
|
+
"_bindata_accepted_parameters_#{param_name}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def ensure_parameter_storage_exists(obj_class, method_name)
|
51
|
+
return if obj_class.instance_methods.include?(method_name)
|
52
|
+
|
53
|
+
iv = "@#{method_name}"
|
54
|
+
obj_class.class_eval <<-END
|
55
|
+
def #{method_name}
|
56
|
+
unless defined? #{iv}
|
57
|
+
ancestor = ancestors[1..-1].find { |a| a.instance_variable_defined?(:#{iv}) }
|
58
|
+
ancestor_params = ancestor.nil? ? nil : ancestor.instance_variable_get(:#{iv})
|
59
|
+
#{iv} = AcceptedParameters.new(ancestor_params)
|
60
|
+
end
|
61
|
+
#{iv}
|
62
|
+
end
|
63
|
+
END
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(ancestor_params = nil)
|
68
|
+
@mandatory = ancestor_params ? ancestor_params.mandatory : []
|
69
|
+
@optional = ancestor_params ? ancestor_params.optional : []
|
70
|
+
@default = ancestor_params ? ancestor_params.default : Hash.new
|
71
|
+
@mutually_exclusive = ancestor_params ? ancestor_params.mutually_exclusive : []
|
72
|
+
end
|
73
|
+
|
74
|
+
def mandatory(*args)
|
75
|
+
if not args.empty?
|
76
|
+
@mandatory.concat(args.collect { |a| a.to_sym })
|
77
|
+
@mandatory.uniq!
|
78
|
+
end
|
79
|
+
@mandatory.dup
|
80
|
+
end
|
81
|
+
|
82
|
+
def optional(*args)
|
83
|
+
if not args.empty?
|
84
|
+
@optional.concat(args.collect { |a| a.to_sym })
|
85
|
+
@optional.uniq!
|
86
|
+
end
|
87
|
+
@optional.dup
|
88
|
+
end
|
22
89
|
|
23
|
-
|
24
|
-
|
25
|
-
|
90
|
+
def default(args = {})
|
91
|
+
if not args.empty?
|
92
|
+
args.each_pair do |k,v|
|
93
|
+
@default[k.to_sym] = v
|
26
94
|
end
|
95
|
+
end
|
96
|
+
@default.dup
|
97
|
+
end
|
98
|
+
|
99
|
+
def mutually_exclusive(*args)
|
100
|
+
arg1, arg2 = args
|
101
|
+
if arg1 != nil && arg2 != nil
|
102
|
+
@mutually_exclusive.push([arg1.to_sym, arg2.to_sym])
|
103
|
+
@mutually_exclusive.uniq!
|
104
|
+
end
|
105
|
+
@mutually_exclusive.dup
|
106
|
+
end
|
107
|
+
|
108
|
+
def all
|
109
|
+
(@mandatory + @optional + @default.keys).uniq
|
110
|
+
end
|
111
|
+
|
112
|
+
def sanitize_parameters!(sanitizer, params)
|
113
|
+
merge_default_parameters!(params)
|
114
|
+
ensure_mandatory_parameters_exist(params)
|
115
|
+
ensure_mutual_exclusion_of_parameters(params)
|
116
|
+
end
|
27
117
|
|
28
|
-
|
29
|
-
|
118
|
+
#---------------
|
119
|
+
private
|
120
|
+
|
121
|
+
def merge_default_parameters!(params)
|
122
|
+
@default.each do |k,v|
|
123
|
+
params[k] = v unless params.has_key?(k)
|
30
124
|
end
|
125
|
+
end
|
31
126
|
|
32
|
-
|
33
|
-
|
127
|
+
def ensure_mandatory_parameters_exist(params)
|
128
|
+
@mandatory.each do |prm|
|
129
|
+
unless params.has_key?(prm)
|
130
|
+
raise ArgumentError, "parameter ':#{prm}' must be specified"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def ensure_mutual_exclusion_of_parameters(params)
|
136
|
+
@mutually_exclusive.each do |param1, param2|
|
137
|
+
if params.has_key?(param1) and params.has_key?(param2)
|
138
|
+
raise ArgumentError, "params ':#{param1}' and ':#{param2}' " +
|
139
|
+
"are mutually exclusive"
|
140
|
+
end
|
141
|
+
end
|
34
142
|
end
|
35
143
|
end
|
36
144
|
end
|