cassandra-driver 3.0.3-java → 3.1.0-java
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/README.md +46 -31
- data/lib/cassandra.rb +35 -44
- data/lib/cassandra/cluster.rb +40 -11
- data/lib/cassandra/cluster/client.rb +193 -159
- data/lib/cassandra/cluster/connector.rb +12 -10
- data/lib/cassandra/cluster/control_connection.rb +38 -10
- data/lib/cassandra/cluster/options.rb +8 -4
- data/lib/cassandra/cluster/registry.rb +1 -2
- data/lib/cassandra/cluster/schema/fetchers.rb +122 -26
- data/lib/cassandra/column_container.rb +9 -4
- data/lib/cassandra/custom_data.rb +24 -22
- data/lib/cassandra/driver.rb +30 -13
- data/lib/cassandra/errors.rb +12 -2
- data/lib/cassandra/execution/options.rb +52 -16
- data/lib/cassandra/execution/profile.rb +150 -0
- data/lib/cassandra/execution/profile_manager.rb +71 -0
- data/lib/cassandra/execution/trace.rb +5 -4
- data/lib/cassandra/executors.rb +1 -1
- data/lib/cassandra/index.rb +1 -1
- data/lib/cassandra/keyspace.rb +36 -1
- data/lib/cassandra/protocol.rb +5 -0
- data/lib/cassandra/protocol/coder.rb +2 -1
- data/lib/cassandra/protocol/cql_byte_buffer.rb +21 -0
- data/lib/cassandra/protocol/responses/read_failure_error_response.rb +10 -4
- data/lib/cassandra/protocol/responses/write_failure_error_response.rb +14 -8
- data/lib/cassandra/protocol/v3.rb +2 -1
- data/lib/cassandra/protocol/v4.rb +58 -20
- data/lib/cassandra/result.rb +1 -1
- data/lib/cassandra/session.rb +43 -16
- data/lib/cassandra/statements/bound.rb +5 -1
- data/lib/cassandra/statements/prepared.rb +8 -3
- data/lib/cassandra/table.rb +72 -0
- data/lib/cassandra/trigger.rb +67 -0
- data/lib/cassandra/types.rb +12 -24
- data/lib/cassandra/udt.rb +3 -6
- data/lib/cassandra/uuid/generator.rb +6 -3
- data/lib/cassandra/version.rb +1 -1
- data/lib/cassandra_murmur3.jar +0 -0
- metadata +5 -2
data/lib/cassandra/result.rb
CHANGED
data/lib/cassandra/session.rb
CHANGED
@@ -29,10 +29,11 @@ module Cassandra
|
|
29
29
|
def_delegators :@client, :keyspace
|
30
30
|
|
31
31
|
# @private
|
32
|
-
def initialize(client, default_options, futures_factory)
|
32
|
+
def initialize(client, default_options, futures_factory, profile_manager)
|
33
33
|
@client = client
|
34
34
|
@options = default_options
|
35
35
|
@futures = futures_factory
|
36
|
+
@profile_manager = profile_manager
|
36
37
|
end
|
37
38
|
|
38
39
|
# Executes a given statement and returns a future result
|
@@ -66,6 +67,8 @@ module Cassandra
|
|
66
67
|
# statement can be retried safely on timeout.
|
67
68
|
# @option options [Hash<[String, Symbol], String>] :payload (nil) custom
|
68
69
|
# outgoing payload to be sent with the request.
|
70
|
+
# @option options [String, Symbol] :execution_profile (nil) name of {Cassandra::Execution::Profile}
|
71
|
+
# from which to obtain certain query options. Defaults to the cluster's default execution profile.
|
69
72
|
#
|
70
73
|
# @see Cassandra.cluster Options that can be specified on the cluster-level
|
71
74
|
# and their default values.
|
@@ -78,14 +81,9 @@ module Cassandra
|
|
78
81
|
#
|
79
82
|
# @return [Cassandra::Future<Cassandra::Result>]
|
80
83
|
#
|
81
|
-
# @see Cassandra::Session#execute A list of errors this future can be
|
82
|
-
# resolved with
|
84
|
+
# @see Cassandra::Session#execute A list of errors this future can be resolved with
|
83
85
|
def execute_async(statement, options = nil)
|
84
|
-
options =
|
85
|
-
@options.override(options)
|
86
|
-
else
|
87
|
-
@options
|
88
|
-
end
|
86
|
+
options = merge_execution_options(options)
|
89
87
|
|
90
88
|
case statement
|
91
89
|
when ::String
|
@@ -110,7 +108,7 @@ module Cassandra
|
|
110
108
|
# Cassandra::Statements::Bound, Cassandra::Statements::Prepared]
|
111
109
|
# statement to execute
|
112
110
|
#
|
113
|
-
# @param options [Hash] (nil) a customizable set of options
|
111
|
+
# @param options [Hash] (nil) a customizable set of options. See {#execute_async} for details.
|
114
112
|
#
|
115
113
|
# @see Cassandra::Session#execute_async
|
116
114
|
# @see Cassandra::Future#get
|
@@ -138,15 +136,13 @@ module Cassandra
|
|
138
136
|
# @option options [Boolean] :idempotent (false) specify whether the
|
139
137
|
# statement being prepared can be retried safely on timeout during
|
140
138
|
# execution.
|
139
|
+
# @option options [String, Symbol] :execution_profile (nil) name of {Cassandra::Execution::Profile}
|
140
|
+
# from which to obtain certain query options. Defaults to the cluster's default execution profile.
|
141
141
|
#
|
142
142
|
# @return [Cassandra::Future<Cassandra::Statements::Prepared>] future
|
143
143
|
# prepared statement
|
144
144
|
def prepare_async(statement, options = nil)
|
145
|
-
options =
|
146
|
-
@options.override(options)
|
147
|
-
else
|
148
|
-
@options
|
149
|
-
end
|
145
|
+
options = merge_execution_options(options)
|
150
146
|
|
151
147
|
case statement
|
152
148
|
when ::String
|
@@ -161,14 +157,20 @@ module Cassandra
|
|
161
157
|
end
|
162
158
|
|
163
159
|
# A blocking wrapper around {Cassandra::Session#prepare_async}
|
160
|
+
#
|
161
|
+
# @param statement [String, Cassandra::Statements::Simple] a statement to
|
162
|
+
# prepare
|
163
|
+
#
|
164
|
+
# @param options [Hash] (nil) a customizable set of options. See {#prepare_async} for details.
|
165
|
+
#
|
164
166
|
# @see Cassandra::Session#prepare_async
|
165
167
|
# @see Cassandra::Future#get
|
166
168
|
#
|
167
169
|
# @return [Cassandra::Statements::Prepared] prepared statement
|
168
170
|
# @raise [Cassandra::Errors::NoHostsAvailable] if none of the hosts can be reached
|
169
171
|
# @raise [Cassandra::Errors::ExecutionError] if Cassandra returns an error response
|
170
|
-
def prepare(
|
171
|
-
prepare_async(
|
172
|
+
def prepare(statement, options = nil)
|
173
|
+
prepare_async(statement, options).get
|
172
174
|
end
|
173
175
|
|
174
176
|
# Returns a logged {Statements::Batch} instance and optionally yields it to
|
@@ -235,5 +237,30 @@ module Cassandra
|
|
235
237
|
"@keyspace=#{keyspace.inspect}, " \
|
236
238
|
"@options=#{@options.inspect}>"
|
237
239
|
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
# @private
|
244
|
+
def merge_execution_options(options)
|
245
|
+
if options
|
246
|
+
Util.assert_instance_of(::Hash, options, "options must be a Hash, #{options.inspect} given")
|
247
|
+
# Yell if the caller gave us a bad profile name.
|
248
|
+
execution_profile = nil
|
249
|
+
if options.key?(:execution_profile)
|
250
|
+
execution_profile = @profile_manager.profiles[options[:execution_profile]]
|
251
|
+
raise ::ArgumentError.new("Unknown execution profile #{options[:execution_profile]}") unless execution_profile
|
252
|
+
end
|
253
|
+
|
254
|
+
# This looks a little hokey, so let's explain: Execution::Options.override takes a
|
255
|
+
# varargs-style array of things to merge into the base options object (to produce
|
256
|
+
# a new Options object, not mutate the base). If an execution profile was specified,
|
257
|
+
# we want its attributes to override the base options. In addition, if individual options
|
258
|
+
# were specified, we want *those* to take precedence over the execution profile attributes.
|
259
|
+
# So we override in this order.
|
260
|
+
@options.override(execution_profile, options)
|
261
|
+
else
|
262
|
+
@options
|
263
|
+
end
|
264
|
+
end
|
238
265
|
end
|
239
266
|
end
|
@@ -28,15 +28,19 @@ module Cassandra
|
|
28
28
|
attr_reader :params
|
29
29
|
# @private
|
30
30
|
attr_reader :params_types, :result_metadata, :keyspace, :partition_key
|
31
|
+
# @private prepared-statement id
|
32
|
+
attr_reader :id
|
31
33
|
|
32
34
|
# @private
|
33
|
-
def initialize(
|
35
|
+
def initialize(id,
|
36
|
+
cql,
|
34
37
|
params_types,
|
35
38
|
result_metadata,
|
36
39
|
params,
|
37
40
|
keyspace = nil,
|
38
41
|
partition_key = nil,
|
39
42
|
idempotent = false)
|
43
|
+
@id = id
|
40
44
|
@cql = cql
|
41
45
|
@params_types = params_types
|
42
46
|
@result_metadata = result_metadata
|
@@ -27,9 +27,12 @@ module Cassandra
|
|
27
27
|
attr_reader :cql
|
28
28
|
# @private
|
29
29
|
attr_reader :result_metadata
|
30
|
+
# @private prepared-statement id
|
31
|
+
attr_reader :id
|
30
32
|
|
31
33
|
# @private
|
32
|
-
def initialize(
|
34
|
+
def initialize(id,
|
35
|
+
payload,
|
33
36
|
warnings,
|
34
37
|
cql,
|
35
38
|
params_metadata,
|
@@ -44,6 +47,7 @@ module Cassandra
|
|
44
47
|
retries,
|
45
48
|
client,
|
46
49
|
connection_options)
|
50
|
+
@id = id
|
47
51
|
@payload = payload
|
48
52
|
@warnings = warnings
|
49
53
|
@cql = cql
|
@@ -131,7 +135,8 @@ module Cassandra
|
|
131
135
|
|
132
136
|
partition_key = create_partition_key(params)
|
133
137
|
|
134
|
-
Bound.new(@
|
138
|
+
Bound.new(@id,
|
139
|
+
@cql,
|
135
140
|
param_types,
|
136
141
|
@result_metadata,
|
137
142
|
params,
|
@@ -151,7 +156,7 @@ module Cassandra
|
|
151
156
|
@consistency,
|
152
157
|
@retries,
|
153
158
|
@trace_id ?
|
154
|
-
Execution::Trace.new(@trace_id, @client) :
|
159
|
+
Execution::Trace.new(@trace_id, @client, @options.load_balancing_policy) :
|
155
160
|
nil)
|
156
161
|
end
|
157
162
|
|
data/lib/cassandra/table.rb
CHANGED
@@ -38,6 +38,10 @@ module Cassandra
|
|
38
38
|
@clustering_order = clustering_order.freeze
|
39
39
|
@indexes = []
|
40
40
|
@indexes_hash = {}
|
41
|
+
@materialized_views = []
|
42
|
+
@materialized_views_hash = {}
|
43
|
+
@triggers = []
|
44
|
+
@triggers_hash = {}
|
41
45
|
end
|
42
46
|
|
43
47
|
# @param name [String] index name
|
@@ -68,6 +72,62 @@ module Cassandra
|
|
68
72
|
end
|
69
73
|
alias indexes each_index
|
70
74
|
|
75
|
+
# @param name [String] trigger name
|
76
|
+
# @return [Boolean] whether this table has a given trigger
|
77
|
+
def has_trigger?(name)
|
78
|
+
@triggers_hash.key?(name)
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param name [String] trigger name
|
82
|
+
# @return [Cassandra::Trigger, nil] a trigger or nil
|
83
|
+
def trigger(name)
|
84
|
+
@triggers_hash[name]
|
85
|
+
end
|
86
|
+
|
87
|
+
# Yield or enumerate each trigger bound to this table
|
88
|
+
# @overload each_trigger
|
89
|
+
# @yieldparam trigger [Cassandra::Index] current trigger
|
90
|
+
# @return [Cassandra::Table] self
|
91
|
+
# @overload each_trigger
|
92
|
+
# @return [Array<Cassandra::Trigger>] a list of triggers
|
93
|
+
def each_trigger(&block)
|
94
|
+
if block_given?
|
95
|
+
@triggers.each(&block)
|
96
|
+
self
|
97
|
+
else
|
98
|
+
@triggers.freeze
|
99
|
+
end
|
100
|
+
end
|
101
|
+
alias triggers each_trigger
|
102
|
+
|
103
|
+
# @param name [String] materialized view name
|
104
|
+
# @return [Boolean] whether this table has a given materialized view
|
105
|
+
def has_materialized_view?(name)
|
106
|
+
@materialized_views_hash.key?(name)
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param name [String] materialized view name
|
110
|
+
# @return [Cassandra::MaterializedView, nil] a materialized view or nil
|
111
|
+
def materialized_view(name)
|
112
|
+
@materialized_views_hash[name]
|
113
|
+
end
|
114
|
+
|
115
|
+
# Yield or enumerate each materialized view bound to this table
|
116
|
+
# @overload each_materialized_view
|
117
|
+
# @yieldparam materialized_view [Cassandra::MaterializedView] current materialized view
|
118
|
+
# @return [Cassandra::Table] self
|
119
|
+
# @overload each_materialized_view
|
120
|
+
# @return [Array<Cassandra::MaterializedView>] a list of materialized views
|
121
|
+
def each_materialized_view(&block)
|
122
|
+
if block_given?
|
123
|
+
@materialized_views.each(&block)
|
124
|
+
self
|
125
|
+
else
|
126
|
+
@materialized_views.freeze
|
127
|
+
end
|
128
|
+
end
|
129
|
+
alias materialized_views each_materialized_view
|
130
|
+
|
71
131
|
# @return [String] a cql representation of this table
|
72
132
|
def to_cql
|
73
133
|
cql = "CREATE TABLE #{Util.escape_name(@keyspace.name)}.#{Util.escape_name(@name)} (\n"
|
@@ -134,6 +194,18 @@ module Cassandra
|
|
134
194
|
@indexes_hash[index.name] = index
|
135
195
|
end
|
136
196
|
|
197
|
+
# @private
|
198
|
+
def add_view(view)
|
199
|
+
@materialized_views << view
|
200
|
+
@materialized_views_hash[view.name] = view
|
201
|
+
end
|
202
|
+
|
203
|
+
# @private
|
204
|
+
def add_trigger(trigger)
|
205
|
+
@triggers << trigger
|
206
|
+
@triggers_hash[trigger.name] = trigger
|
207
|
+
end
|
208
|
+
|
137
209
|
# @private
|
138
210
|
def eql?(other)
|
139
211
|
other.is_a?(Table) &&
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright 2013-2016 DataStax, Inc.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#++
|
18
|
+
|
19
|
+
module Cassandra
|
20
|
+
# Represents a trigger on a cassandra table
|
21
|
+
class Trigger
|
22
|
+
# @return [Cassandra::Table] table that the trigger applies to.
|
23
|
+
attr_reader :table
|
24
|
+
# @return [String] name of the trigger.
|
25
|
+
attr_reader :name
|
26
|
+
# @return [Hash] options of the trigger.
|
27
|
+
attr_reader :options
|
28
|
+
|
29
|
+
# @private
|
30
|
+
def initialize(table,
|
31
|
+
name,
|
32
|
+
options)
|
33
|
+
@table = table
|
34
|
+
@name = name.freeze
|
35
|
+
@options = options.freeze
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [String] name of the trigger class
|
39
|
+
def custom_class_name
|
40
|
+
@options['class']
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [String] a cql representation of this trigger
|
44
|
+
def to_cql
|
45
|
+
keyspace_name = Util.escape_name(@table.keyspace.name)
|
46
|
+
table_name = Util.escape_name(@table.name)
|
47
|
+
trigger_name = Util.escape_name(@name)
|
48
|
+
|
49
|
+
"CREATE TRIGGER #{trigger_name} ON #{keyspace_name}.#{table_name} USING '#{@options['class']}';"
|
50
|
+
end
|
51
|
+
|
52
|
+
# @private
|
53
|
+
def eql?(other)
|
54
|
+
other.is_a?(Trigger) &&
|
55
|
+
@table == other.table &&
|
56
|
+
@name == other.name &&
|
57
|
+
@options == other.options
|
58
|
+
end
|
59
|
+
alias == eql?
|
60
|
+
|
61
|
+
# @private
|
62
|
+
def inspect
|
63
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} " \
|
64
|
+
"@name=#{@name.inspect} @table=#{@table.inspect} @options=#{@options.inspect}>"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/cassandra/types.rb
CHANGED
@@ -1518,8 +1518,7 @@ module Cassandra
|
|
1518
1518
|
# @return [Cassandra::Types::List] list type
|
1519
1519
|
def list(value_type)
|
1520
1520
|
Util.assert_instance_of(Cassandra::Type, value_type,
|
1521
|
-
"list type must be a Cassandra::Type, #{value_type.inspect} given"
|
1522
|
-
)
|
1521
|
+
"list type must be a Cassandra::Type, #{value_type.inspect} given")
|
1523
1522
|
|
1524
1523
|
List.new(value_type)
|
1525
1524
|
end
|
@@ -1529,11 +1528,9 @@ module Cassandra
|
|
1529
1528
|
# @return [Cassandra::Types::Map] map type
|
1530
1529
|
def map(key_type, value_type)
|
1531
1530
|
Util.assert_instance_of(Cassandra::Type, key_type,
|
1532
|
-
"map key type must be a Cassandra::Type, #{key_type.inspect} given"
|
1533
|
-
)
|
1531
|
+
"map key type must be a Cassandra::Type, #{key_type.inspect} given")
|
1534
1532
|
Util.assert_instance_of(Cassandra::Type, value_type,
|
1535
|
-
"map value type must be a Cassandra::Type, #{value_type.inspect} given"
|
1536
|
-
)
|
1533
|
+
"map value type must be a Cassandra::Type, #{value_type.inspect} given")
|
1537
1534
|
|
1538
1535
|
Map.new(key_type, value_type)
|
1539
1536
|
end
|
@@ -1542,8 +1539,7 @@ module Cassandra
|
|
1542
1539
|
# @return [Cassandra::Types::Set] set type
|
1543
1540
|
def set(value_type)
|
1544
1541
|
Util.assert_instance_of(Cassandra::Type, value_type,
|
1545
|
-
"set type must be a Cassandra::Type, #{value_type.inspect} given"
|
1546
|
-
)
|
1542
|
+
"set type must be a Cassandra::Type, #{value_type.inspect} given")
|
1547
1543
|
|
1548
1544
|
Set.new(value_type)
|
1549
1545
|
end
|
@@ -1555,8 +1551,7 @@ module Cassandra
|
|
1555
1551
|
members.each do |member|
|
1556
1552
|
Util.assert_instance_of(Cassandra::Type, member,
|
1557
1553
|
'each tuple member must be a Cassandra::Type, ' \
|
1558
|
-
"#{member.inspect} given"
|
1559
|
-
)
|
1554
|
+
"#{member.inspect} given")
|
1560
1555
|
end
|
1561
1556
|
|
1562
1557
|
Tuple.new(*members)
|
@@ -1598,40 +1593,33 @@ module Cassandra
|
|
1598
1593
|
fields = Array(fields.first) if fields.one?
|
1599
1594
|
|
1600
1595
|
Util.assert_not_empty(fields,
|
1601
|
-
'user-defined type must contain at least one field'
|
1602
|
-
)
|
1596
|
+
'user-defined type must contain at least one field')
|
1603
1597
|
|
1604
1598
|
if fields.first.is_a?(::Array)
|
1605
1599
|
fields = fields.map do |pair|
|
1606
1600
|
Util.assert(pair.size == 2,
|
1607
1601
|
'fields of a user-defined type must be an Array of name and ' \
|
1608
|
-
"value pairs, #{pair.inspect} given"
|
1609
|
-
)
|
1602
|
+
"value pairs, #{pair.inspect} given")
|
1610
1603
|
Util.assert_instance_of(::String, pair[0],
|
1611
1604
|
'each field name for a user-defined type must be a String, ' \
|
1612
|
-
"#{pair[0].inspect} given"
|
1613
|
-
)
|
1605
|
+
"#{pair[0].inspect} given")
|
1614
1606
|
Util.assert_instance_of(Cassandra::Type, pair[1],
|
1615
1607
|
'each field type for a user-defined type must be a ' \
|
1616
|
-
"Cassandra::Type, #{pair[1].inspect} given"
|
1617
|
-
)
|
1608
|
+
"Cassandra::Type, #{pair[1].inspect} given")
|
1618
1609
|
|
1619
1610
|
UserDefined::Field.new(*pair)
|
1620
1611
|
end
|
1621
1612
|
else
|
1622
1613
|
Util.assert(fields.size.even?,
|
1623
1614
|
'fields of a user-defined type must be an Array of alternating ' \
|
1624
|
-
"names and values pairs, #{fields.inspect} given"
|
1625
|
-
)
|
1615
|
+
"names and values pairs, #{fields.inspect} given")
|
1626
1616
|
fields = fields.each_slice(2).map do |field_name, field_type|
|
1627
1617
|
Util.assert_instance_of(::String, field_name,
|
1628
1618
|
'each field name for a user-defined type must be a String, ' \
|
1629
|
-
"#{field_name.inspect} given"
|
1630
|
-
)
|
1619
|
+
"#{field_name.inspect} given")
|
1631
1620
|
Util.assert_instance_of(Cassandra::Type, field_type,
|
1632
1621
|
'each field type for a user-defined type must be a ' \
|
1633
|
-
"Cassandra::Type, #{field_type.inspect} given"
|
1634
|
-
)
|
1622
|
+
"Cassandra::Type, #{field_type.inspect} given")
|
1635
1623
|
|
1636
1624
|
UserDefined::Field.new(field_name, field_type)
|
1637
1625
|
end
|
data/lib/cassandra/udt.rb
CHANGED
@@ -224,15 +224,13 @@ module Cassandra
|
|
224
224
|
values = Array(values.first) if values.one?
|
225
225
|
|
226
226
|
Util.assert_not_empty(values,
|
227
|
-
'user-defined type must contain at least one value'
|
228
|
-
)
|
227
|
+
'user-defined type must contain at least one value')
|
229
228
|
|
230
229
|
if values.first.is_a?(::Array)
|
231
230
|
@values = values.map do |pair|
|
232
231
|
Util.assert(pair.size == 2,
|
233
232
|
'values of a user-defined type must be an Array of name and ' \
|
234
|
-
"value pairs, #{pair.inspect} given"
|
235
|
-
)
|
233
|
+
"value pairs, #{pair.inspect} given")
|
236
234
|
name, value = pair
|
237
235
|
|
238
236
|
[String(name), value]
|
@@ -240,8 +238,7 @@ module Cassandra
|
|
240
238
|
else
|
241
239
|
Util.assert(values.size.even?,
|
242
240
|
'values of a user-defined type must be an Array of alternating ' \
|
243
|
-
"names and values pairs, #{values.inspect} given"
|
244
|
-
)
|
241
|
+
"names and values pairs, #{values.inspect} given")
|
245
242
|
@values = values.each_slice(2).map do |(name, value)|
|
246
243
|
[String(name), value]
|
247
244
|
end
|