unobtainium 0.3.5 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|