page_magic 2.0.0.alpha1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -2
- data/.zsh_config +5 -5
- data/Dockerfile +2 -1
- data/Gemfile +2 -1
- data/Gemfile.lock +9 -5
- data/Makefile +7 -3
- data/README.md +16 -4
- data/VERSION +1 -1
- data/lib/active_support/core_ext/object/to_query.rb +6 -6
- data/lib/page_magic.rb +15 -16
- data/lib/page_magic/class_methods.rb +1 -1
- data/lib/page_magic/comparator.rb +37 -0
- data/lib/page_magic/comparator/fuzzy.rb +23 -0
- data/lib/page_magic/comparator/literal.rb +22 -0
- data/lib/page_magic/comparator/null.rb +26 -0
- data/lib/page_magic/comparator/parameter_map.rb +52 -0
- data/lib/page_magic/drivers.rb +2 -2
- data/lib/page_magic/element.rb +19 -8
- data/lib/page_magic/element/locators.rb +4 -4
- data/lib/page_magic/element/not_found.rb +38 -0
- data/lib/page_magic/element/query.rb +19 -27
- data/lib/page_magic/element/query/multiple_results.rb +21 -0
- data/lib/page_magic/element/query/prefetched_result.rb +26 -0
- data/lib/page_magic/element/query/single_result.rb +20 -0
- data/lib/page_magic/element/selector.rb +38 -16
- data/lib/page_magic/element/selector/methods.rb +18 -0
- data/lib/page_magic/element/selector/model.rb +21 -0
- data/lib/page_magic/element_context.rb +5 -21
- data/lib/page_magic/element_definition_builder.rb +17 -24
- data/lib/page_magic/elements.rb +62 -102
- data/lib/page_magic/elements/config.rb +103 -0
- data/lib/page_magic/elements/inheritance_hooks.rb +15 -0
- data/lib/page_magic/elements/types.rb +25 -0
- data/lib/page_magic/exceptions.rb +3 -0
- data/lib/page_magic/instance_methods.rb +2 -2
- data/lib/page_magic/mapping.rb +79 -0
- data/lib/page_magic/session.rb +10 -32
- data/lib/page_magic/session_methods.rb +1 -1
- data/lib/page_magic/transitions.rb +49 -0
- data/lib/page_magic/utils/string.rb +4 -0
- data/lib/page_magic/utils/url.rb +20 -0
- data/lib/page_magic/watcher.rb +10 -17
- data/lib/page_magic/watchers.rb +28 -15
- data/spec/page_magic/class_methods_spec.rb +64 -37
- data/spec/page_magic/comparator/fuzzy_spec.rb +44 -0
- data/spec/page_magic/comparator/literal_spec.rb +41 -0
- data/spec/page_magic/comparator/null_spec.rb +35 -0
- data/spec/page_magic/comparator/parameter_map_spec.rb +75 -0
- data/spec/page_magic/driver_spec.rb +25 -29
- data/spec/page_magic/drivers/poltergeist_spec.rb +4 -7
- data/spec/page_magic/drivers/rack_test_spec.rb +4 -9
- data/spec/page_magic/drivers/selenium_spec.rb +9 -12
- data/spec/page_magic/drivers_spec.rb +36 -29
- data/spec/page_magic/element/locators_spec.rb +26 -25
- data/spec/page_magic/element/not_found_spec.rb +24 -0
- data/spec/page_magic/element/query/multiple_results_spec.rb +14 -0
- data/spec/page_magic/element/query/single_result_spec.rb +21 -0
- data/spec/page_magic/element/query_spec.rb +26 -47
- data/spec/page_magic/element/selector_spec.rb +118 -110
- data/spec/page_magic/element_context_spec.rb +46 -88
- data/spec/page_magic/element_definition_builder_spec.rb +12 -71
- data/spec/page_magic/element_spec.rb +256 -0
- data/spec/page_magic/elements/config_spec.rb +200 -0
- data/spec/page_magic/elements_spec.rb +87 -138
- data/spec/page_magic/instance_methods_spec.rb +63 -63
- data/spec/page_magic/mapping_spec.rb +181 -0
- data/spec/page_magic/session_methods_spec.rb +27 -25
- data/spec/page_magic/session_spec.rb +109 -198
- data/spec/page_magic/transitions_spec.rb +43 -0
- data/spec/page_magic/utils/string_spec.rb +20 -27
- data/spec/page_magic/utils/url_spec.rb +9 -0
- data/spec/page_magic/wait_methods_spec.rb +14 -22
- data/spec/page_magic/watcher_spec.rb +22 -0
- data/spec/page_magic/watchers_spec.rb +56 -62
- data/spec/page_magic_spec.rb +27 -24
- data/spec/spec_helper.rb +7 -3
- data/spec/support/shared_examples.rb +15 -17
- metadata +48 -15
- data/lib/page_magic/element/query_builder.rb +0 -61
- data/lib/page_magic/element/selector_methods.rb +0 -16
- data/lib/page_magic/matcher.rb +0 -130
- data/spec/element_spec.rb +0 -251
- data/spec/page_magic/element/query_builder_spec.rb +0 -110
- data/spec/page_magic/matcher_spec.rb +0 -338
- data/spec/support/shared_contexts/files_context.rb +0 -9
- data/spec/support/shared_contexts/nested_elements_html_context.rb +0 -18
- data/spec/support/shared_contexts/rack_application_context.rb +0 -11
- data/spec/support/shared_contexts/webapp_fixture_context.rb +0 -41
- data/spec/watcher_spec.rb +0 -64
data/lib/page_magic/elements.rb
CHANGED
@@ -1,162 +1,122 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support/inflector'
|
4
|
-
|
4
|
+
require_relative 'element_definition_builder'
|
5
|
+
require_relative 'elements/inheritance_hooks'
|
6
|
+
require_relative 'elements/config'
|
7
|
+
require_relative 'elements/types'
|
8
|
+
|
5
9
|
module PageMagic
|
6
10
|
# module Elements - contains methods that add element definitions to the objects it is mixed in to
|
7
11
|
module Elements
|
8
|
-
# hooks for objects that inherit classes that include the Elements module
|
9
|
-
module InheritanceHooks
|
10
|
-
# Copies parent element definitions on to subclass
|
11
|
-
# @param [Class] clazz - inheritting class
|
12
|
-
def inherited(clazz)
|
13
|
-
clazz.element_definitions.merge!(element_definitions)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
12
|
INVALID_METHOD_NAME_MSG = 'a method already exists with this method name'
|
18
13
|
|
19
|
-
# css
|
20
|
-
# datalist_input
|
21
|
-
# datalist_option
|
22
|
-
# field
|
23
|
-
# fieldset
|
24
|
-
# file_field
|
25
|
-
# fillable_field
|
26
|
-
# frame
|
27
|
-
# link_or_button
|
28
|
-
# option
|
29
|
-
# radio_button
|
30
|
-
# select
|
31
|
-
# table
|
32
|
-
# table_row
|
33
|
-
# xpath
|
34
|
-
#
|
35
|
-
|
36
|
-
TYPES = %i[field
|
37
|
-
fieldset
|
38
|
-
file_field
|
39
|
-
fillable_field
|
40
|
-
frame
|
41
|
-
link_or_button
|
42
|
-
option
|
43
|
-
radio_button
|
44
|
-
select
|
45
|
-
table
|
46
|
-
table_row
|
47
|
-
text_field
|
48
|
-
button
|
49
|
-
link
|
50
|
-
checkbox
|
51
|
-
select_list
|
52
|
-
radio
|
53
|
-
textarea
|
54
|
-
label].collect{ |type| [type, :"#{type}s"]}.flatten.freeze
|
55
|
-
|
56
14
|
class << self
|
57
15
|
def extended(clazz)
|
58
16
|
clazz.extend(InheritanceHooks)
|
59
17
|
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def define_element_methods(types)
|
22
|
+
types.each { |type| alias_method type, :element }
|
23
|
+
end
|
24
|
+
|
25
|
+
def define_pluralised_element_methods(types)
|
26
|
+
types.each { |type| alias_method type.to_s.pluralize, :elements }
|
27
|
+
end
|
60
28
|
end
|
61
29
|
|
62
|
-
# Creates an
|
63
|
-
# Element defintions contain specifications for locating them and other sub elements.
|
64
|
-
# if a block is specified then it will be executed against the element defintion.
|
30
|
+
# Creates an Element definition
|
65
31
|
# This method is aliased to each of the names specified in {TYPES TYPES}
|
32
|
+
# Element definitions contain specifications for locating them and other sub elements.
|
33
|
+
# @yield if a block is specified then it will be executed against the element definition.
|
66
34
|
# @example
|
67
35
|
# element :widget, id: 'widget' do
|
68
36
|
# link :next, text: 'next'
|
69
37
|
# end
|
70
38
|
# @overload element(name, selector, &block)
|
71
39
|
# @param [Symbol] name the name of the element.
|
72
|
-
# @param [Hash] selector a key value pair defining the method for locating this element
|
40
|
+
# @param [Hash<Symbol,String>] selector a key value pair defining the method for locating this element
|
73
41
|
# @option selector [String] :text text contained within the element
|
74
42
|
# @option selector [String] :css css selector
|
75
43
|
# @option selector [String] :id the id of the element
|
76
44
|
# @option selector [String] :name the value of the name attribute belonging to the element
|
77
45
|
# @option selector [String] :label value of the label tied to the require field
|
46
|
+
# @param optional [Hash<Symbol,String>] capybara_options
|
78
47
|
# @overload element(element_class, &block)
|
79
48
|
# @param [ElementClass] element_class a custom class of element that inherits {Element}.
|
80
49
|
# the name of the element is derived from the class name. the Class name coverted to snakecase.
|
81
50
|
# The selector must be defined on the class itself.
|
51
|
+
# @param optional [Hash<Symbol,String>] capybara_options
|
82
52
|
# @overload element(name, element_class, &block)
|
83
53
|
# @param [Symbol] name the name of the element.
|
84
54
|
# @param [ElementClass] element_class a custom class of element that inherits {Element}.
|
85
55
|
# The selector must be defined on the class itself.
|
56
|
+
# @param optional [Hash<Symbol,String>] capybara_options
|
86
57
|
# @overload element(name, element_class, selector, &block)
|
87
58
|
# @param [Symbol] name the name of the element.
|
88
59
|
# @param [ElementClass] element_class a custom class of element that inherits {Element}.
|
89
|
-
# @param [Hash]
|
90
|
-
def element(*args, &block)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
definition_class = Class.new(section_class) do
|
98
|
-
parent_element(parent_element)
|
99
|
-
class_exec(*e_args, &block)
|
100
|
-
end
|
60
|
+
# @param optional [Hash<Symbol,String>] capybara_options
|
61
|
+
def element(*args, **capybara_options, &block)
|
62
|
+
define_element(*args,
|
63
|
+
type: __callee__,
|
64
|
+
query_class: PageMagic::Element::Query::SingleResult,
|
65
|
+
**capybara_options,
|
66
|
+
&block)
|
67
|
+
end
|
101
68
|
|
102
|
-
|
103
|
-
|
69
|
+
# see docs for {Elements#element}
|
70
|
+
def elements(*args, **capybara_options, &block)
|
71
|
+
define_element(*args,
|
72
|
+
type: __callee__.to_s.singularize.to_sym,
|
73
|
+
query_class: PageMagic::Element::Query::MultipleResults,
|
74
|
+
**capybara_options,
|
75
|
+
&block)
|
104
76
|
end
|
105
77
|
|
106
|
-
|
107
|
-
TYPES
|
78
|
+
define_element_methods(TYPES)
|
79
|
+
define_pluralised_element_methods(TYPES)
|
108
80
|
|
109
|
-
# @return [Hash] element definition names mapped to
|
110
|
-
#
|
81
|
+
# @return [Hash<Symbol,ElementDefinitionBuilder>] element definition names mapped to
|
82
|
+
# blocks that can be used to create unique instances of {Element} definitions
|
111
83
|
def element_definitions
|
112
84
|
@element_definitions ||= {}
|
113
85
|
end
|
114
86
|
|
115
87
|
private
|
116
88
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
type: type,
|
122
|
-
selector: compute_selector(args, section_class),
|
123
|
-
options: compute_argument(args, Hash),
|
124
|
-
element: args.delete_at(0),
|
125
|
-
section_class: section_class }.tap do |hash|
|
126
|
-
hash[:options][:multiple_results] = type.to_s.end_with?('s')
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def add_element_definition(name, &block)
|
131
|
-
raise InvalidElementNameException, 'duplicate page element defined' if element_definitions[name]
|
132
|
-
|
133
|
-
methods = respond_to?(:instance_methods) ? instance_methods : methods()
|
134
|
-
raise InvalidElementNameException, INVALID_METHOD_NAME_MSG if methods.find { |method| method == name }
|
135
|
-
|
136
|
-
element_definitions[name] = block
|
137
|
-
end
|
89
|
+
def define_element(*args, type:, query_class:, **capybara_options, &block)
|
90
|
+
block ||= proc {}
|
91
|
+
args << capybara_options unless capybara_options.empty?
|
92
|
+
config = validate!(args, type)
|
138
93
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
94
|
+
element_definitions[config.name] = proc do |parent_element, *e_args|
|
95
|
+
config.definition_class = Class.new(config.element_class) do
|
96
|
+
parent_element(parent_element)
|
97
|
+
class_exec(*e_args, &block)
|
98
|
+
end
|
143
99
|
|
144
|
-
|
145
|
-
|
146
|
-
selector || section_class.selector if section_class.respond_to?(:selector)
|
100
|
+
ElementDefinitionBuilder.new(query_class: query_class, **config.element_options)
|
101
|
+
end
|
147
102
|
end
|
148
103
|
|
149
104
|
def method_added(method)
|
105
|
+
super
|
150
106
|
raise InvalidMethodNameException, 'method name matches element name' if element_definitions[method]
|
151
107
|
end
|
152
108
|
|
153
|
-
def
|
154
|
-
|
109
|
+
def validate!(args, type)
|
110
|
+
config = Config.build(args, type).validate!
|
111
|
+
validate_element_name(config.name)
|
112
|
+
config
|
155
113
|
end
|
156
114
|
|
157
|
-
def
|
158
|
-
|
159
|
-
|
115
|
+
def validate_element_name(name)
|
116
|
+
raise InvalidElementNameException, 'duplicate page element defined' if element_definitions[name]
|
117
|
+
|
118
|
+
methods = respond_to?(:instance_methods) ? instance_methods : methods()
|
119
|
+
raise InvalidElementNameException, INVALID_METHOD_NAME_MSG if methods.find { |method| method == name }
|
160
120
|
end
|
161
121
|
end
|
162
122
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PageMagic
|
4
|
+
module Elements
|
5
|
+
CONFIG_STRUCT = Struct.new(:name,
|
6
|
+
:definition_class,
|
7
|
+
:type,
|
8
|
+
:selector,
|
9
|
+
:options,
|
10
|
+
:element,
|
11
|
+
:element_class,
|
12
|
+
keyword_init: true)
|
13
|
+
|
14
|
+
# class Config - use to validate input to {PageMagic::Elements#elment}
|
15
|
+
class Config < CONFIG_STRUCT
|
16
|
+
INVALID_SELECTOR_MSG = 'Pass a locator/define one on the class'
|
17
|
+
INVALID_ELEMENT_CLASS_MSG = 'Element class must be of type `PageMagic::Element`'
|
18
|
+
TYPE_REQUIRED_MESSAGE = 'element type required'
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Create `Config` used to build instances `PageMagic::Element` see `Page::Elements#element` for details
|
22
|
+
# @param [Args<Object>] args arguments passed to `Page::Elements#element`
|
23
|
+
# @return [Config]
|
24
|
+
def build(args, type)
|
25
|
+
element_class = remove_argument(args, Class) || Element
|
26
|
+
new(
|
27
|
+
name: compute_name(args, element_class),
|
28
|
+
type: type_for(type),
|
29
|
+
selector: compute_selector(args, element_class),
|
30
|
+
options: compute_argument(args, Hash),
|
31
|
+
element: args.delete_at(0),
|
32
|
+
element_class: element_class
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def compute_name(args, element_class)
|
39
|
+
name = remove_argument(args, Symbol)
|
40
|
+
name || element_class.name.demodulize.underscore.to_sym unless element_class.is_a?(Element)
|
41
|
+
end
|
42
|
+
|
43
|
+
def compute_selector(args, element_class)
|
44
|
+
selector = remove_argument(args, Hash)
|
45
|
+
selector || element_class.selector if element_class.respond_to?(:selector)
|
46
|
+
end
|
47
|
+
|
48
|
+
def compute_argument(args, clazz)
|
49
|
+
remove_argument(args, clazz) || clazz.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def remove_argument(args, clazz)
|
53
|
+
argument = args.find { |arg| arg.is_a?(clazz) }
|
54
|
+
args.delete(argument)
|
55
|
+
end
|
56
|
+
|
57
|
+
def type_for(type)
|
58
|
+
field?(type) ? :field : type
|
59
|
+
end
|
60
|
+
|
61
|
+
def field?(type)
|
62
|
+
%i[ text_field checkbox select_list radio textarea field file_field fillable_field
|
63
|
+
radio_button select].include?(type)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Options for the building of `PageMagic::Element` via `PageMagic::ElementDefinitionBuilder#new`
|
68
|
+
# @return [Hash<Symbol,Object>]
|
69
|
+
def element_options
|
70
|
+
to_h.except(:element_class, :name, :type, :options).update(selector: selector)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Selector built using supplied configuration
|
74
|
+
# @return [PageMagic::Element::Selector::Model]
|
75
|
+
def selector
|
76
|
+
selector = self[:selector]
|
77
|
+
Element::Selector.find(selector.keys.first).build(type, selector.values.first, options: options)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Validate supplied configuration
|
81
|
+
# @raise [PageMagic::InvalidConfigurationException]
|
82
|
+
# @return [PageMagic::Elements::Config]
|
83
|
+
def validate!
|
84
|
+
raise PageMagic::InvalidConfigurationException, INVALID_SELECTOR_MSG unless element || valid_selector?
|
85
|
+
raise PageMagic::InvalidConfigurationException, 'element type required' unless type
|
86
|
+
raise PageMagic::InvalidConfigurationException, INVALID_ELEMENT_CLASS_MSG unless valid_element_class?
|
87
|
+
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def valid_selector?
|
94
|
+
selector = self[:selector]
|
95
|
+
selector.is_a?(Hash) && !selector.empty?
|
96
|
+
end
|
97
|
+
|
98
|
+
def valid_element_class?
|
99
|
+
element_class && (element_class == PageMagic::Element || element_class < PageMagic::Element)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PageMagic
|
4
|
+
module Elements
|
5
|
+
# hooks for objects that inherit classes that include the Elements module
|
6
|
+
module InheritanceHooks
|
7
|
+
# Copies parent element definitions on to subclass
|
8
|
+
# @param [Class] clazz - inheriting class
|
9
|
+
def inherited(clazz)
|
10
|
+
super
|
11
|
+
clazz.element_definitions.merge!(element_definitions)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PageMagic
|
4
|
+
module Elements
|
5
|
+
TYPES = %i[field
|
6
|
+
fieldset
|
7
|
+
file_field
|
8
|
+
fillable_field
|
9
|
+
frame
|
10
|
+
link_or_button
|
11
|
+
option
|
12
|
+
radio_button
|
13
|
+
select
|
14
|
+
table
|
15
|
+
table_row
|
16
|
+
text_field
|
17
|
+
button
|
18
|
+
link
|
19
|
+
checkbox
|
20
|
+
select_list
|
21
|
+
radio
|
22
|
+
textarea
|
23
|
+
label].freeze
|
24
|
+
end
|
25
|
+
end
|
@@ -19,7 +19,7 @@ module PageMagic
|
|
19
19
|
@browser_element = browser
|
20
20
|
end
|
21
21
|
|
22
|
-
# @return [Array] class level defined element definitions
|
22
|
+
# @return [Array<ElementDefinitionBuilder>] class level defined element definitions
|
23
23
|
def element_definitions
|
24
24
|
self.class.element_definitions
|
25
25
|
end
|
@@ -37,7 +37,7 @@ module PageMagic
|
|
37
37
|
element_context.send(method, *args)
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
40
|
+
def respond_to_missing?(*args)
|
41
41
|
contains_element?(args.first) || super
|
42
42
|
end
|
43
43
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../active_support/core_ext/object/to_query'
|
4
|
+
require_relative 'comparator'
|
5
|
+
|
6
|
+
|
7
|
+
module PageMagic
|
8
|
+
# models mapping used to relate pages to uris
|
9
|
+
class Mapping
|
10
|
+
attr_reader :path, :parameters, :fragment
|
11
|
+
|
12
|
+
# @param [Object] path String or Regular expression to match with
|
13
|
+
# @param [Hash] parameters mapping of parameter name to literal or regex to match with
|
14
|
+
# @param [Object] fragment String or Regular expression to match with
|
15
|
+
# @raise [MatcherInvalidException] if at least one component is not specified
|
16
|
+
def initialize(path = nil, parameters: {}, fragment: nil)
|
17
|
+
raise MatcherInvalidException unless path || parameters || fragment
|
18
|
+
|
19
|
+
@path = Comparator.for(path)
|
20
|
+
@parameters = Comparator.for(parameters)
|
21
|
+
@fragment = Comparator.for(fragment)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] true if no component contains a Regexp
|
25
|
+
def can_compute_uri?
|
26
|
+
!fragment.fuzzy? && !path.fuzzy? && !parameters.fuzzy?
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [String] uri represented by this mapping
|
30
|
+
def compute_uri
|
31
|
+
path.to_s.dup.tap do |uri|
|
32
|
+
uri << "?#{parameters.comparator.to_query}" unless parameters.empty?
|
33
|
+
uri << "##{fragment}" if fragment.present?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Fixnum] hash for instance
|
38
|
+
def hash
|
39
|
+
[path, parameters, fragment].hash
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [String] uri
|
43
|
+
# @return [Boolean] returns true if the uri is matched against this matcher
|
44
|
+
def match?(uri)
|
45
|
+
uri = URI(uri)
|
46
|
+
path.match?(uri.path) && parameters.match?(parameters_hash(uri.query)) && fragment.match?(uri.fragment)
|
47
|
+
end
|
48
|
+
|
49
|
+
# compare this matcher against another
|
50
|
+
# @param [Mapping] other
|
51
|
+
# @return [Fixnum] -1 = smaller, 0 = equal to, 1 = greater than
|
52
|
+
def <=>(other)
|
53
|
+
path_comparison = path <=> other.path
|
54
|
+
return path_comparison unless path_comparison.zero?
|
55
|
+
|
56
|
+
parameter_comparison = parameters <=> other.parameters
|
57
|
+
return parameter_comparison unless parameter_comparison.zero?
|
58
|
+
|
59
|
+
fragment <=> other.fragment
|
60
|
+
end
|
61
|
+
|
62
|
+
# check equality
|
63
|
+
# @param [Mapping] other
|
64
|
+
# @return [Boolean]
|
65
|
+
def ==(other)
|
66
|
+
return false unless other.is_a?(Mapping)
|
67
|
+
|
68
|
+
path == other.path && parameters == other.parameters && fragment == other.fragment
|
69
|
+
end
|
70
|
+
|
71
|
+
alias eql? ==
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def parameters_hash(string)
|
76
|
+
CGI.parse(string.to_s.downcase).collect { |key, value| [key.downcase, value.first] }.to_h
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|