appium_lib_core 4.5.0 → 5.0.0.beta4
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/.github/workflows/unittest.yml +2 -2
- data/CHANGELOG.md +12 -5
- data/README.md +2 -1
- data/appium_lib_core.gemspec +4 -4
- data/lib/appium_lib_core.rb +2 -5
- data/lib/appium_lib_core/android/device/auth_finger_print.rb +2 -1
- data/lib/appium_lib_core/common/base.rb +0 -3
- data/lib/appium_lib_core/common/base/bridge.rb +284 -89
- data/lib/appium_lib_core/common/base/capabilities.rb +3 -3
- data/lib/appium_lib_core/common/base/{command.rb → device_ime.rb} +32 -7
- data/lib/appium_lib_core/common/base/driver.rb +128 -70
- data/lib/appium_lib_core/common/base/driver_settings.rb +51 -0
- data/lib/appium_lib_core/common/base/has_network_connection.rb +56 -0
- data/lib/appium_lib_core/common/base/rotable.rb +1 -1
- data/lib/appium_lib_core/common/base/search_context.rb +9 -4
- data/lib/appium_lib_core/common/command.rb +259 -4
- data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
- data/lib/appium_lib_core/common/error.rb +4 -1
- data/lib/appium_lib_core/common/log.rb +4 -1
- data/lib/appium_lib_core/device.rb +1 -5
- data/lib/appium_lib_core/driver.rb +3 -4
- data/lib/appium_lib_core/{patch.rb → element.rb} +2 -7
- data/lib/appium_lib_core/ios/uiautomation/patch.rb +1 -1
- data/lib/appium_lib_core/version.rb +2 -2
- data/release_notes.md +14 -0
- data/script/commands.rb +3 -37
- metadata +21 -30
- data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -91
- data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -248
- data/lib/appium_lib_core/common/command/common.rb +0 -110
- data/lib/appium_lib_core/common/command/mjsonwp.rb +0 -28
- data/lib/appium_lib_core/common/command/w3c.rb +0 -56
@@ -19,10 +19,10 @@ module Appium
|
|
19
19
|
# @private
|
20
20
|
# @param [Hash] opts_caps Capabilities for Appium server. All capability keys are converted to lowerCamelCase when
|
21
21
|
# this client sends capabilities to Appium server as JSON format.
|
22
|
-
# @return [::Selenium::WebDriver::Remote::
|
23
|
-
# inherited ::Selenium::WebDriver::Remote::
|
22
|
+
# @return [::Selenium::WebDriver::Remote::Capabilities] Return instance of Appium::Core::Base::Capabilities
|
23
|
+
# inherited ::Selenium::WebDriver::Remote::Capabilities
|
24
24
|
def self.create_capabilities(opts_caps = {})
|
25
|
-
::Selenium::WebDriver::Remote::
|
25
|
+
::Selenium::WebDriver::Remote::Capabilities.new(opts_caps)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -15,10 +15,35 @@
|
|
15
15
|
module Appium
|
16
16
|
module Core
|
17
17
|
class Base
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
#
|
21
|
+
class DeviceIME
|
22
|
+
# @private this class is private
|
23
|
+
def initialize(bridge)
|
24
|
+
@bridge = bridge
|
25
|
+
end
|
26
|
+
|
27
|
+
def activate(ime_name)
|
28
|
+
@bridge.ime_activate(ime_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def available_engines
|
32
|
+
@bridge.ime_available_engines
|
33
|
+
end
|
34
|
+
|
35
|
+
def active_engine
|
36
|
+
@bridge.ime_active_engine
|
37
|
+
end
|
38
|
+
|
39
|
+
def activated?
|
40
|
+
@bridge.ime_activated
|
41
|
+
end
|
42
|
+
|
43
|
+
def deactivate
|
44
|
+
@bridge.ime_deactivate
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -13,11 +13,14 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
require 'base64'
|
16
|
+
require_relative 'device_ime'
|
17
|
+
require_relative 'driver_settings'
|
16
18
|
require_relative 'search_context'
|
17
19
|
require_relative 'screenshot'
|
18
20
|
require_relative 'rotable'
|
19
21
|
require_relative 'remote_status'
|
20
22
|
require_relative 'has_location'
|
23
|
+
require_relative 'has_network_connection'
|
21
24
|
|
22
25
|
module Appium
|
23
26
|
module Core
|
@@ -27,31 +30,63 @@ module Appium
|
|
27
30
|
include ::Selenium::WebDriver::DriverExtensions::HasSessionId
|
28
31
|
include ::Selenium::WebDriver::DriverExtensions::HasRemoteStatus
|
29
32
|
include ::Selenium::WebDriver::DriverExtensions::HasWebStorage
|
30
|
-
include ::Selenium::WebDriver::DriverExtensions::HasNetworkConnection
|
31
|
-
include ::Selenium::WebDriver::DriverExtensions::HasTouchScreen
|
32
33
|
|
33
34
|
include ::Appium::Core::Base::Rotatable
|
34
35
|
include ::Appium::Core::Base::SearchContext
|
35
36
|
include ::Appium::Core::Base::TakesScreenshot
|
36
37
|
include ::Appium::Core::Base::HasRemoteStatus
|
37
38
|
include ::Appium::Core::Base::HasLocation
|
39
|
+
include ::Appium::Core::Base::HasNetworkConnection
|
40
|
+
|
41
|
+
private
|
38
42
|
|
39
43
|
# Private API.
|
40
44
|
# Do not use this for general use. Used by flutter driver to get bridge for creating a new element
|
41
45
|
attr_reader :bridge
|
42
46
|
|
43
|
-
def initialize(
|
44
|
-
|
45
|
-
@bridge = ::Appium::Core::Base::Bridge.handshake(**opts)
|
46
|
-
super(@bridge, listener: listener)
|
47
|
+
def initialize(bridge: nil, listener: nil, **opts)
|
48
|
+
super
|
47
49
|
end
|
48
50
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
|
52
|
-
|
51
|
+
# Implements protocol handshake which:
|
52
|
+
#
|
53
|
+
# 1. Creates session with driver.
|
54
|
+
# 2. Sniffs response.
|
55
|
+
# 3. Based on the response, understands which dialect we should use.
|
56
|
+
#
|
57
|
+
# @return [::Appium::Core::Base::Bridge]
|
58
|
+
#
|
59
|
+
def create_bridge(**opts)
|
60
|
+
# TODO: probably Appium does not need this.
|
61
|
+
opts[:url] ||= service_url(opts)
|
62
|
+
caps = opts.delete(:capabilities)
|
63
|
+
# NOTE: This is deprecated
|
64
|
+
cap_array = caps.is_a?(Hash) ? [caps] : Array(caps)
|
65
|
+
|
66
|
+
desired_capabilities = opts.delete(:desired_capabilities)
|
67
|
+
if desired_capabilities
|
68
|
+
if desired_capabilities.is_a?(Hash)
|
69
|
+
desired_capabilities = ::Selenium::WebDriver::Remote::Capabilities(desired_capabilities)
|
70
|
+
end
|
71
|
+
cap_array << desired_capabilities
|
72
|
+
end
|
73
|
+
|
74
|
+
options = opts.delete(:options)
|
75
|
+
cap_array << options if options
|
76
|
+
|
77
|
+
capabilities = generate_capabilities(cap_array)
|
78
|
+
|
79
|
+
bridge_opts = { http_client: opts.delete(:http_client), url: opts.delete(:url) }
|
80
|
+
raise ::Appium::Core::Error::ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
|
81
|
+
|
82
|
+
bridge = ::Appium::Core::Base::Bridge.new(**bridge_opts)
|
83
|
+
|
84
|
+
bridge.create_session(capabilities)
|
85
|
+
bridge
|
53
86
|
end
|
54
87
|
|
88
|
+
public
|
89
|
+
|
55
90
|
# Update +server_url+ and HTTP clients following this arguments, protocol, host, port and path.
|
56
91
|
# After this method, +@bridge.http+ will be a new instance following them instead of +server_url+ which is
|
57
92
|
# set before creating session.
|
@@ -76,6 +111,75 @@ module Appium
|
|
76
111
|
path: path)
|
77
112
|
end
|
78
113
|
|
114
|
+
AVAILABLE_METHODS = [
|
115
|
+
:get, :head, :post, :put, :delete,
|
116
|
+
:connect, :options, :trace, :patch
|
117
|
+
].freeze
|
118
|
+
# Define a new custom method to the driver so that you can define your own method for
|
119
|
+
# drivers/plugins in Appium 2.0. Appium 2.0 and its custom drivers/plugins allow you
|
120
|
+
# to define custom commands that are not part of W3C spec.
|
121
|
+
#
|
122
|
+
# @param [Symbol] method HTTP request method as https://www.w3.org/TR/webdriver/#endpoints
|
123
|
+
# @param [string] url The url to URL template as https://www.w3.org/TR/webdriver/#endpoints.
|
124
|
+
# +:session_id+ is the placeholder of 'session id'.
|
125
|
+
# Other place holders can be specified with +:+ prefix like +:id+.
|
126
|
+
# Then, the +:id+ will be replaced with a given value as the seconds argument of +execute+
|
127
|
+
# @param [Symbol] name The name of method that is called as the driver instance method.
|
128
|
+
# @param [Proc] block The block to involve as the method
|
129
|
+
# @raise [ArgumentError] If the given +name+ is already defined or +method+ are invalid value.
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
#
|
133
|
+
# @driver.add_command(
|
134
|
+
# method: :get,
|
135
|
+
# url: 'session/:session_id/path/to/custom/url',
|
136
|
+
# name: :test_command
|
137
|
+
# )
|
138
|
+
# # Send a GET request to 'session/<session id>/path/to/custom/url'
|
139
|
+
# @driver.test_command
|
140
|
+
#
|
141
|
+
#
|
142
|
+
# @driver.add_command(
|
143
|
+
# method: :post,
|
144
|
+
# url: 'session/:session_id/path/to/custom/url',
|
145
|
+
# name: :test_command
|
146
|
+
# ) do
|
147
|
+
# def test_command(argument)
|
148
|
+
# execute(:test_command, {}, { dummy: argument })
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
# # Send a POST request to 'session/<session id>/path/to/custom/url'
|
152
|
+
# # with body "{ dummy: 1 }" as JSON object. "1" is the argument.
|
153
|
+
# # ':session_id' in the given 'url' is replaced with current 'session id'.
|
154
|
+
# @driver.test_command(1)
|
155
|
+
#
|
156
|
+
# @driver.add_command(
|
157
|
+
# method: :post,
|
158
|
+
# url: 'session/:session_id/element/:id/custom/action',
|
159
|
+
# name: :test_action_command
|
160
|
+
# ) do
|
161
|
+
# def test_action_command(element_id, action)
|
162
|
+
# execute(:test_action_command, {id: element_id}, { dummy_action: action })
|
163
|
+
# end
|
164
|
+
# end
|
165
|
+
# # Send a POST request to 'session/<session id>/element/<element id>/custom/action'
|
166
|
+
# # with body "{ dummy_action: #{action} }" as JSON object. "action" is the seconds argument.
|
167
|
+
# # ':session_id' in the given url is replaced with current 'session id'.
|
168
|
+
# # ':id' in the given url is replaced with the given 'element_id'.
|
169
|
+
# e = @driver.find_element :accessibility_id, 'an element'
|
170
|
+
# @driver.test_action_command(e.ref, 'action')
|
171
|
+
#
|
172
|
+
def add_command(method:, url:, name:, &block)
|
173
|
+
unless AVAILABLE_METHODS.include? method
|
174
|
+
raise ::Appium::Core::Error::ArgumentError, "Available method is either #{AVAILABLE_METHODS}"
|
175
|
+
end
|
176
|
+
|
177
|
+
# TODO: Remove this logger before Appium 2.0 release
|
178
|
+
::Appium::Logger.info '[Experimental] this method is experimental for Appium 2.0. This interface may change.'
|
179
|
+
|
180
|
+
@bridge.add_command method: method, url: url, name: name, &block
|
181
|
+
end
|
182
|
+
|
79
183
|
### Methods for Appium
|
80
184
|
|
81
185
|
# Lock the device
|
@@ -158,22 +262,6 @@ module Appium
|
|
158
262
|
end
|
159
263
|
alias type send_keys
|
160
264
|
|
161
|
-
class DriverSettings
|
162
|
-
# @private this class is private
|
163
|
-
def initialize(bridge)
|
164
|
-
@bridge = bridge
|
165
|
-
end
|
166
|
-
|
167
|
-
def get
|
168
|
-
@bridge.get_settings
|
169
|
-
end
|
170
|
-
|
171
|
-
def update(settings)
|
172
|
-
@bridge.update_settings(settings)
|
173
|
-
end
|
174
|
-
end
|
175
|
-
private_constant :DriverSettings
|
176
|
-
|
177
265
|
# Returns an instance of DriverSettings to call get/update.
|
178
266
|
#
|
179
267
|
# @example
|
@@ -182,7 +270,7 @@ module Appium
|
|
182
270
|
# @driver.settings.update('allowInvisibleElements': true)
|
183
271
|
#
|
184
272
|
def settings
|
185
|
-
@
|
273
|
+
@settings ||= DriverSettings.new(@bridge)
|
186
274
|
end
|
187
275
|
|
188
276
|
# Get appium Settings for current test session.
|
@@ -204,8 +292,8 @@ module Appium
|
|
204
292
|
#
|
205
293
|
# @example
|
206
294
|
#
|
207
|
-
# @driver.update_settings('allowInvisibleElements': true)
|
208
|
-
# @driver.settings.update('allowInvisibleElements': true)
|
295
|
+
# @driver.update_settings({ 'allowInvisibleElements': true })
|
296
|
+
# @driver.settings.update({ 'allowInvisibleElements': true })
|
209
297
|
# @driver.settings = { 'allowInvisibleElements': true }
|
210
298
|
#
|
211
299
|
def settings=(value)
|
@@ -213,34 +301,6 @@ module Appium
|
|
213
301
|
end
|
214
302
|
alias update_settings settings=
|
215
303
|
|
216
|
-
class DeviceIME
|
217
|
-
# @private this class is private
|
218
|
-
def initialize(bridge)
|
219
|
-
@bridge = bridge
|
220
|
-
end
|
221
|
-
|
222
|
-
def activate(ime_name)
|
223
|
-
@bridge.ime_activate(ime_name)
|
224
|
-
end
|
225
|
-
|
226
|
-
def available_engines
|
227
|
-
@bridge.ime_available_engines
|
228
|
-
end
|
229
|
-
|
230
|
-
def active_engine
|
231
|
-
@bridge.ime_active_engine
|
232
|
-
end
|
233
|
-
|
234
|
-
def activated?
|
235
|
-
@bridge.ime_activated
|
236
|
-
end
|
237
|
-
|
238
|
-
def deactivate
|
239
|
-
@bridge.ime_deactivate
|
240
|
-
end
|
241
|
-
end
|
242
|
-
private_constant :DeviceIME
|
243
|
-
|
244
304
|
# Returns an instance of DeviceIME
|
245
305
|
#
|
246
306
|
# @return [Appium::Core::Base::Driver::DeviceIME]
|
@@ -254,7 +314,7 @@ module Appium
|
|
254
314
|
# @driver.ime.deactivate #=> Deactivate current IME engine
|
255
315
|
#
|
256
316
|
def ime
|
257
|
-
@
|
317
|
+
@ime ||= DeviceIME.new(@bridge)
|
258
318
|
end
|
259
319
|
|
260
320
|
# Android only. Make an engine that is available active.
|
@@ -817,7 +877,7 @@ module Appium
|
|
817
877
|
# @driver.perform_actions [f1, f2] #=> 'nil' if the action succeed
|
818
878
|
#
|
819
879
|
def perform_actions(data)
|
820
|
-
raise ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
|
880
|
+
raise ::Appium::Core::Error::ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
|
821
881
|
|
822
882
|
@bridge.send_actions data.map(&:encode).compact
|
823
883
|
data.each(&:clear_actions)
|
@@ -887,16 +947,15 @@ module Appium
|
|
887
947
|
# Retrieve the capabilities of the specified session.
|
888
948
|
# It's almost same as +@driver.capabilities+ but you can get more details.
|
889
949
|
#
|
890
|
-
# @return [Selenium::WebDriver::Remote::Capabilities, Selenium::WebDriver::Remote::
|
950
|
+
# @return [Selenium::WebDriver::Remote::Capabilities, Selenium::WebDriver::Remote::Capabilities]
|
891
951
|
#
|
892
952
|
# @example
|
893
953
|
# @driver.session_capabilities
|
894
954
|
#
|
895
955
|
# #=> uiautomator2
|
896
|
-
# # <Selenium::WebDriver::Remote::
|
956
|
+
# # <Selenium::WebDriver::Remote::Capabilities:0x007fa38dae1360
|
897
957
|
# # @capabilities=
|
898
|
-
# # {:
|
899
|
-
# # :browser_name=>nil,
|
958
|
+
# # {:browser_name=>nil,
|
900
959
|
# # :browser_version=>nil,
|
901
960
|
# # :platform_name=>"android",
|
902
961
|
# # :page_load_strategy=>nil,
|
@@ -943,10 +1002,9 @@ module Appium
|
|
943
1002
|
# # "viewportRect"=>{"left"=>0, "top"=>63, "width"=>1080, "height"=>1731}}>
|
944
1003
|
# #
|
945
1004
|
# #=> XCUITest
|
946
|
-
# # <Selenium::WebDriver::Remote::
|
1005
|
+
# # <Selenium::WebDriver::Remote::Capabilities:0x007fb15dc01370
|
947
1006
|
# # @capabilities=
|
948
|
-
# # {:
|
949
|
-
# # :browser_name=>"UICatalog",
|
1007
|
+
# # {:browser_name=>"UICatalog",
|
950
1008
|
# # :browser_version=>nil,
|
951
1009
|
# # :platform_name=>"ios",
|
952
1010
|
# # :page_load_strategy=>nil,
|
@@ -1027,7 +1085,7 @@ module Appium
|
|
1027
1085
|
#
|
1028
1086
|
# @param [String] img_path A path to a partial image you'd like to find
|
1029
1087
|
#
|
1030
|
-
# @return [::
|
1088
|
+
# @return [::Appium::Core::Element]
|
1031
1089
|
#
|
1032
1090
|
# @example
|
1033
1091
|
#
|
@@ -1097,14 +1155,14 @@ module Appium
|
|
1097
1155
|
@bridge.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
|
1098
1156
|
end
|
1099
1157
|
|
1100
|
-
# Convert vanilla element response to ::
|
1158
|
+
# Convert vanilla element response to ::Appium::Core::Element
|
1101
1159
|
#
|
1102
1160
|
# @param [Hash] id The id which can get as a response from server
|
1103
|
-
# @return [::
|
1161
|
+
# @return [::Appium::Core::Element]
|
1104
1162
|
#
|
1105
1163
|
# @example
|
1106
1164
|
# response = {"element-6066-11e4-a52e-4f735466cecf"=>"xxxx", "ELEMENT"=>"xxxx"}
|
1107
|
-
# ele = @driver.convert_to_element(response) #=> ::
|
1165
|
+
# ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
|
1108
1166
|
# ele.rect #=> Can get the rect of the element
|
1109
1167
|
#
|
1110
1168
|
def convert_to_element(id)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Appium
|
16
|
+
module Core
|
17
|
+
class Base
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
#
|
21
|
+
class DriverSettings
|
22
|
+
# @private this class is private
|
23
|
+
def initialize(bridge)
|
24
|
+
@bridge = bridge
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get appium Settings for current test session.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
#
|
31
|
+
# @driver.settings.get
|
32
|
+
#
|
33
|
+
def get
|
34
|
+
@bridge.get_settings
|
35
|
+
end
|
36
|
+
|
37
|
+
# Update Appium Settings for current test session
|
38
|
+
#
|
39
|
+
# @param [Hash] settings Settings to update, keys are settings, values to value to set each setting to
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
#
|
43
|
+
# @driver.settings.update({'allowInvisibleElements': true})
|
44
|
+
#
|
45
|
+
def update(settings)
|
46
|
+
@bridge.update_settings(settings)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Appium
|
16
|
+
module Core
|
17
|
+
class Base
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
#
|
21
|
+
module HasNetworkConnection
|
22
|
+
def network_connection_type
|
23
|
+
connection_value = @bridge.network_connection
|
24
|
+
|
25
|
+
connection_type = values_to_type[connection_value]
|
26
|
+
|
27
|
+
# In case the connection type is not recognized return the
|
28
|
+
# connection value.
|
29
|
+
connection_type || connection_value
|
30
|
+
end
|
31
|
+
|
32
|
+
def network_connection_type=(connection_type)
|
33
|
+
raise ::Appium::Core::Error::ArgumentError, 'Invalid connection type' unless valid_type? connection_type
|
34
|
+
|
35
|
+
connection_value = type_to_values[connection_type]
|
36
|
+
|
37
|
+
@bridge.network_connection = connection_value
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def type_to_values
|
43
|
+
{ airplane_mode: 1, wifi: 2, data: 4, all: 6, none: 0 }
|
44
|
+
end
|
45
|
+
|
46
|
+
def values_to_type
|
47
|
+
type_to_values.invert
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_type?(type)
|
51
|
+
type_to_values.keys.include? type
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|