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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 869c9481a4c19f67aad597d81ea044008012a2a3
4
- data.tar.gz: 61af815e9e9ec60a432966efd2b0c7348848341f
3
+ metadata.gz: 97948bc83a14acdbe6c71e330e66e907d2a19fe7
4
+ data.tar.gz: 33ad8b54a05cd91a90935896378bdba28d9f12cb
5
5
  SHA512:
6
- metadata.gz: 1e1309efacab5bd98dcb243eba59f3c8327537d5006449d73ed68e3d78e2112936386ac4f2bb635fa9e9d636389cfc4123ac79a7a2feebb3c0b52247d654d100
7
- data.tar.gz: e7a91f3b0a399159d691bed04a0648967cdd16835041c2aec8e4dfa454c5a66e3f3cfb8bee7db273a1cc49bab2b06274f99ba7b6a08ecd87b228adf2b7484ae5
6
+ metadata.gz: 63da85ce1551f33687e11cbb1d59577303907797e8c0cf1a935d692d9922cfb8cf8ace0f30bdd4badc5f97245cc5449127f734952865e3121bc0c58f60266987
7
+ data.tar.gz: a737bd06d28b979976cd56ac4bd82ab7f918b21d32b6a84a036bb0b1706e9a6103580fa0acef97ce60533d161d5eb45fd77515df3c8e99e84438278b639954e8
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- unobtainium (0.3.5)
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.3)
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.11)
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.11.2
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
- ## Development
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.
@@ -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 respond_to?(meth)
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 respond_to?(meth)
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
@@ -8,5 +8,5 @@
8
8
  #
9
9
  module Unobtainium
10
10
  # The current release version
11
- VERSION = "0.3.5".freeze
11
+ VERSION = "0.4.0".freeze
12
12
  end
@@ -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
@@ -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.11"
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.3.5
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-04-27 00:00:00.000000000 Z
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.11'
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.11'
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