unobtainium 0.3.5 → 0.4.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -4
- data/README.md +12 -1
- data/docs/DRIVER_MODULES.md +66 -0
- data/lib/unobtainium/driver.rb +50 -2
- data/lib/unobtainium/pathed_hash.rb +2 -2
- data/lib/unobtainium/version.rb +1 -1
- data/spec/driver_spec.rb +71 -1
- data/unobtainium.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97948bc83a14acdbe6c71e330e66e907d2a19fe7
|
4
|
+
data.tar.gz: 33ad8b54a05cd91a90935896378bdba28d9f12cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63da85ce1551f33687e11cbb1d59577303907797e8c0cf1a935d692d9922cfb8cf8ace0f30bdd4badc5f97245cc5449127f734952865e3121bc0c58f60266987
|
7
|
+
data.tar.gz: a737bd06d28b979976cd56ac4bd82ab7f918b21d32b6a84a036bb0b1706e9a6103580fa0acef97ce60533d161d5eb45fd77515df3c8e99e84438278b639954e8
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
unobtainium (0.
|
4
|
+
unobtainium (0.4.0)
|
5
5
|
sys-proctable (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -79,7 +79,7 @@ GEM
|
|
79
79
|
simplecov-html (0.10.0)
|
80
80
|
sys-proctable (1.0.0)
|
81
81
|
tomlrb (1.2.1)
|
82
|
-
unicode-display_width (1.0.
|
82
|
+
unicode-display_width (1.0.5)
|
83
83
|
websocket (1.2.3)
|
84
84
|
yard (0.8.7.6)
|
85
85
|
|
@@ -88,7 +88,7 @@ PLATFORMS
|
|
88
88
|
|
89
89
|
DEPENDENCIES
|
90
90
|
appium_lib
|
91
|
-
bundler (~> 1.
|
91
|
+
bundler (~> 1.12)
|
92
92
|
codeclimate-test-reporter
|
93
93
|
cucumber
|
94
94
|
phantomjs
|
@@ -101,4 +101,4 @@ DEPENDENCIES
|
|
101
101
|
yard (~> 0.8)
|
102
102
|
|
103
103
|
BUNDLED WITH
|
104
|
-
1.
|
104
|
+
1.12.1
|
data/README.md
CHANGED
@@ -79,9 +79,20 @@ The configuration file knows two configuration variables:
|
|
79
79
|
See the documentation on [configuration features](docs/CONFIGURATION.md) for
|
80
80
|
details.
|
81
81
|
|
82
|
-
|
82
|
+
# Development
|
83
83
|
|
84
84
|
- [driver development](docs/DRIVERS.md)
|
85
|
+
- [driver module development](docs/DRIVER_MODULES.md)
|
86
|
+
|
87
|
+
# Additional Drivers
|
88
|
+
|
89
|
+
- [unobtainium-nokogiri](https://github.com/jfinkhaeuser/unobtainium-nokogiri) is
|
90
|
+
a nokogiri-based driver for entirely browserless access to XML and HTML files
|
91
|
+
and pages.
|
92
|
+
- [unobtainium-faraday](https://github.com/jfinkhaeuser/unobtainium-faraday) is
|
93
|
+
a faraday-based driver for dealing with RESTish APIs.
|
94
|
+
- [unobtainium-kramdown](https://github.com/jfinkhaeuser/unobtainium-kramdown) is
|
95
|
+
a faraday-based driver for dealing with Markdown structured text.
|
85
96
|
|
86
97
|
# Credits
|
87
98
|
This gem is inspired by [LapisLazuli](https://github.com/spriteCloud/lapis-lazuli),
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Driver Modules
|
2
|
+
|
3
|
+
Driver modules are a way for extending drivers from other gems. It really is
|
4
|
+
little more than a glorified version of Ruby's `extend` mechanism, but fairly
|
5
|
+
convenient.
|
6
|
+
|
7
|
+
Suppose you're using any of the built-in drivers with the Selenium API. That
|
8
|
+
API is fairly verbose when it comes to e.g. handling waiting for an element to
|
9
|
+
appear. You have to create a `Wait` object and use it to loop over `#find_element`
|
10
|
+
until a timeout occurs or the latter returns an element.
|
11
|
+
|
12
|
+
Much simpler to just have a `#wait` method, no?
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
module WaitModule
|
16
|
+
def wait
|
17
|
+
# some clever implementation
|
18
|
+
end # wait
|
19
|
+
end # module WaitModule
|
20
|
+
```
|
21
|
+
|
22
|
+
You can just extend the driver that unobtainium returns, of course:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
drv = driver(:firefox)
|
26
|
+
drv.extend(WaitModule)
|
27
|
+
```
|
28
|
+
|
29
|
+
However, you will have to do this for every driver you instanciate. So let's
|
30
|
+
simplify this a bit.
|
31
|
+
|
32
|
+
## Module Registration
|
33
|
+
|
34
|
+
Instead of having to extend every driver yourself, unobtainium allows you to
|
35
|
+
register your `WaitModule` with the `Driver` class, and unobtainium takes care
|
36
|
+
of the extension for you:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
::Unobtainium::Driver.register_module(WaitModule, __FILE__)
|
40
|
+
drv = driver(:firefox)
|
41
|
+
drv.respond_to?(:wait) # => true
|
42
|
+
```
|
43
|
+
|
44
|
+
## Module Matching
|
45
|
+
|
46
|
+
The only problem with the above is that our hypothetical `#wait` function relies
|
47
|
+
heavily on the driver behaving like Selenium, having e.g. a `#find_element`
|
48
|
+
function. So unobtainium also allows your module implementation to decide whether
|
49
|
+
it wants to extend a particular driver instance or not.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
module WaitModule
|
53
|
+
class << self
|
54
|
+
def matches?(impl)
|
55
|
+
# Only extend drivers with (at least) a `#find_element` method.
|
56
|
+
impl.respond_to?(:find_element)
|
57
|
+
end
|
58
|
+
end # class << self
|
59
|
+
|
60
|
+
def wait
|
61
|
+
# some clever implementation
|
62
|
+
end # wait
|
63
|
+
end # module WaitModule
|
64
|
+
```
|
65
|
+
|
66
|
+
In fact, `#matches?` is a mandatory method for module implementations.
|
data/lib/unobtainium/driver.rb
CHANGED
@@ -70,6 +70,41 @@ module Unobtainium
|
|
70
70
|
@@drivers[klass] = fpath
|
71
71
|
end
|
72
72
|
|
73
|
+
##
|
74
|
+
# Add a new driver module. The first parameter is the class itself, the
|
75
|
+
# second should be a file path pointing to the file where the class is
|
76
|
+
# defined. You would typically pass `__FILE__` for the second parameter.
|
77
|
+
#
|
78
|
+
# Driver modules must implement the class methods listed in `MODULE_METHODS`.
|
79
|
+
#
|
80
|
+
# @param klass (Class) Driver implementation class to register.
|
81
|
+
# @param path (String) Implementation path of the driver class.
|
82
|
+
def register_module(klass, path)
|
83
|
+
# We need to deal with absolute paths only
|
84
|
+
fpath = File.absolute_path(path)
|
85
|
+
|
86
|
+
# Figure out if the class implements all the methods we need; we're not
|
87
|
+
# checking for anything else.
|
88
|
+
klass_methods = klass.methods - klass.instance_methods - Object.methods
|
89
|
+
|
90
|
+
if MODULE_METHODS - klass_methods != []
|
91
|
+
raise LoadError, "Driver module #{klass.name} is not implementing all "\
|
92
|
+
"of the class methods #{MODULE_METHODS}, aborting!"
|
93
|
+
end
|
94
|
+
|
95
|
+
# The second question is whether the same class is already known, or
|
96
|
+
# whether a class with the same name but under a different location is
|
97
|
+
# known.
|
98
|
+
if @@modules.include?(klass) and @@modules[klass] != fpath
|
99
|
+
raise LoadError, "Driver module #{klass.name} is duplicated in file "\
|
100
|
+
"'#{fpath}'; previous definition is here: "\
|
101
|
+
"'#{@@modules[klass]}'"
|
102
|
+
end
|
103
|
+
|
104
|
+
# If all of that was ok, we can register the implementation.
|
105
|
+
@@modules[klass] = fpath
|
106
|
+
end
|
107
|
+
|
73
108
|
private :new
|
74
109
|
|
75
110
|
##
|
@@ -174,8 +209,8 @@ module Unobtainium
|
|
174
209
|
|
175
210
|
##
|
176
211
|
# Map any missing method to the driver implementation
|
177
|
-
def
|
178
|
-
if not @impl.nil? and @impl.respond_to?(meth)
|
212
|
+
def respond_to_missing?(meth, include_private = false)
|
213
|
+
if not @impl.nil? and @impl.respond_to?(meth, include_private)
|
179
214
|
return true
|
180
215
|
end
|
181
216
|
return super
|
@@ -205,12 +240,20 @@ module Unobtainium
|
|
205
240
|
|
206
241
|
# Great, instanciate!
|
207
242
|
@impl = driver_klass.create(@label, @options)
|
243
|
+
|
244
|
+
# Now also extend this implementation with all the modues that match
|
245
|
+
@@modules.each do |klass, _|
|
246
|
+
if klass.matches?(@impl)
|
247
|
+
@impl.extend(klass)
|
248
|
+
end
|
249
|
+
end
|
208
250
|
end
|
209
251
|
|
210
252
|
# Class variables have their place, rubocop... still, err on the strict
|
211
253
|
# side and just skip this check here.
|
212
254
|
# rubocop:disable Style/ClassVars
|
213
255
|
@@drivers = {}
|
256
|
+
@@modules = {}
|
214
257
|
# rubocop:enable Style/ClassVars
|
215
258
|
|
216
259
|
# Methods that drivers must implement
|
@@ -219,5 +262,10 @@ module Unobtainium
|
|
219
262
|
:ensure_preconditions,
|
220
263
|
:create
|
221
264
|
].freeze
|
265
|
+
|
266
|
+
# Methods that driver modules must implement
|
267
|
+
MODULE_METHODS = [
|
268
|
+
:matches?
|
269
|
+
].freeze
|
222
270
|
end # class Driver
|
223
271
|
end # module Unobtainium
|
@@ -154,8 +154,8 @@ module Unobtainium
|
|
154
154
|
|
155
155
|
##
|
156
156
|
# Map any missing method to the Hash implementation
|
157
|
-
def
|
158
|
-
if not @data.nil? and @data.respond_to?(meth)
|
157
|
+
def respond_to_missing?(meth, include_private = false)
|
158
|
+
if not @data.nil? and @data.respond_to?(meth, include_private)
|
159
159
|
return true
|
160
160
|
end
|
161
161
|
return super
|
data/lib/unobtainium/version.rb
CHANGED
data/spec/driver_spec.rb
CHANGED
@@ -5,6 +5,33 @@ require_relative './mock_driver.rb'
|
|
5
5
|
class FakeDriver
|
6
6
|
end # class FakeDriver
|
7
7
|
|
8
|
+
module TestModule
|
9
|
+
class << self
|
10
|
+
def matches?(_)
|
11
|
+
# Always match!
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end # class << self
|
15
|
+
|
16
|
+
def my_module_func
|
17
|
+
end
|
18
|
+
end # module TestModule
|
19
|
+
|
20
|
+
module NonMatchingTestModule
|
21
|
+
class << self
|
22
|
+
def matches?(_)
|
23
|
+
# Never match!
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end # class << self
|
27
|
+
|
28
|
+
def does_not_exist
|
29
|
+
end
|
30
|
+
end # module NonMatchingTestModule
|
31
|
+
|
32
|
+
module FakeModule
|
33
|
+
end # module FakeModule
|
34
|
+
|
8
35
|
describe ::Unobtainium::Driver do
|
9
36
|
before :each do
|
10
37
|
::Unobtainium::Driver.register_implementation(MockDriver, "mock_driver.rb")
|
@@ -18,7 +45,8 @@ describe ::Unobtainium::Driver do
|
|
18
45
|
|
19
46
|
it "refuses to register the same driver twice from different locations" do
|
20
47
|
expect do
|
21
|
-
::Unobtainium::Driver.register_implementation(MockDriver, __FILE__)
|
48
|
+
::Unobtainium::Driver.register_implementation(MockDriver, __FILE__ + '1')
|
49
|
+
::Unobtainium::Driver.register_implementation(MockDriver, __FILE__ + '2')
|
22
50
|
end.to raise_error(LoadError)
|
23
51
|
end
|
24
52
|
|
@@ -62,4 +90,46 @@ describe ::Unobtainium::Driver do
|
|
62
90
|
drv = ::Unobtainium::Driver.create(:mock, foo: 42)
|
63
91
|
expect(drv.passed_options).to eql foo: 42
|
64
92
|
end
|
93
|
+
|
94
|
+
describe 'modules' do
|
95
|
+
it 'will register a module' do
|
96
|
+
expect do
|
97
|
+
::Unobtainium::Driver.register_module(TestModule, __FILE__)
|
98
|
+
end.not_to raise_error(LoadError)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'refuses to register the same module twice' do
|
102
|
+
expect do
|
103
|
+
::Unobtainium::Driver.register_module(TestModule, __FILE__ + '1')
|
104
|
+
::Unobtainium::Driver.register_module(TestModule, __FILE__ + '2')
|
105
|
+
end.to raise_error(LoadError)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'refuses to register a module with the wrong interface' do
|
109
|
+
expect do
|
110
|
+
::Unobtainium::Driver.register_module(FakeModule, __FILE__)
|
111
|
+
end.to raise_error(LoadError)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'extends a driver with a registered module' do
|
115
|
+
expect do
|
116
|
+
::Unobtainium::Driver.register_module(TestModule, __FILE__)
|
117
|
+
end.not_to raise_error(LoadError)
|
118
|
+
|
119
|
+
drv = ::Unobtainium::Driver.create(:mock)
|
120
|
+
|
121
|
+
expect(drv.respond_to?(:my_module_func)).to be_truthy
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'does not extend a driver with a non-matching module' do
|
125
|
+
expect do
|
126
|
+
::Unobtainium::Driver.register_module(TestModule, __FILE__)
|
127
|
+
::Unobtainium::Driver.register_module(NonMatchingTestModule, __FILE__)
|
128
|
+
end.not_to raise_error(LoadError)
|
129
|
+
|
130
|
+
drv = ::Unobtainium::Driver.create(:mock)
|
131
|
+
|
132
|
+
expect(drv.respond_to?(:does_not_exist)).to be_falsy
|
133
|
+
end
|
134
|
+
end
|
65
135
|
end
|
data/unobtainium.gemspec
CHANGED
@@ -42,7 +42,7 @@ Gem::Specification.new do |spec|
|
|
42
42
|
spec.requirements = "Either or all of 'selenium-webdriver', 'appium_lib', "\
|
43
43
|
"'phantomjs'"
|
44
44
|
|
45
|
-
spec.add_development_dependency "bundler", "~> 1.
|
45
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
46
46
|
spec.add_development_dependency "rubocop", "~> 0.39"
|
47
47
|
spec.add_development_dependency "rake", "~> 11.1"
|
48
48
|
spec.add_development_dependency "rspec", "~> 3.4"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unobtainium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jens Finkhaeuser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.12'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.12'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rubocop
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -186,6 +186,7 @@ files:
|
|
186
186
|
- config/config.yml
|
187
187
|
- docs/CONFIGURATION.md
|
188
188
|
- docs/DRIVERS.md
|
189
|
+
- docs/DRIVER_MODULES.md
|
189
190
|
- features/step_definitions/steps.rb
|
190
191
|
- features/support/env.rb
|
191
192
|
- features/world.feature
|