unobtainium 0.3.0 → 0.3.1

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: 9921b8f7735c0d00216208fe5ad193525671fd8f
4
- data.tar.gz: 1557e2461fc29570eb97f975d83a4b792b838862
3
+ metadata.gz: 3aa9b74f8f1f3c7d408438a0cff087c268dc89d4
4
+ data.tar.gz: 31f3f35f5ff7b1eccf2fd121c233b62d0056c386
5
5
  SHA512:
6
- metadata.gz: bd7643c1a36ea61aa917629a922cd3535b408f70a4206e18d77cebcd11327e0cb13931589eef0b7367c9c243928fd9c23ea82196397fce2bc756db9edbed1f85
7
- data.tar.gz: f435d50ed4e50e697dbe1b7b1a3c0ddf5ccc50c2148d4d02cb5fea2ac30e7d738e59c47371655a4ccb449bb56c24a66760abebd7838fc22dd0cdecd973c00af6
6
+ metadata.gz: 953d864dd1286c499979044503a8655426f9e14912878d8ebd9300a3ddc9492143c8efe2f014168a44fe7131286107ba318c3c0509591832fda7fd67611dd667
7
+ data.tar.gz: a5bfa05e01c37fd7a8f6a6e15d05227f9552971d708d65fa4a54a290d742d50f0152f95ad55cf9dc51bd6bcf5ee7c44cfc2574caf85cf32069cda8f20ed2fe04
data/.rubocop.yml CHANGED
@@ -64,3 +64,6 @@ Style/TrailingUnderscoreVariable:
64
64
 
65
65
  Style/NumericLiterals:
66
66
  Enabled: false
67
+
68
+ Style/FileName:
69
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- unobtainium (0.2.1)
4
+ unobtainium (0.3.1)
5
5
  sys-proctable (~> 1.0)
6
6
 
7
7
  GEM
@@ -37,11 +37,11 @@ GEM
37
37
  gherkin (3.2.0)
38
38
  json (1.8.3)
39
39
  mini_portile2 (2.0.0)
40
- multi_json (1.11.2)
40
+ multi_json (1.11.3)
41
41
  multi_test (0.1.2)
42
42
  nokogiri (1.6.7.2)
43
43
  mini_portile2 (~> 2.0.0.rc2)
44
- parser (2.3.0.7)
44
+ parser (2.3.1.0)
45
45
  ast (~> 2.2)
46
46
  phantomjs (2.1.1.0)
47
47
  powerpack (0.1.1)
@@ -66,7 +66,7 @@ GEM
66
66
  rainbow (>= 1.99.1, < 3.0)
67
67
  ruby-progressbar (~> 1.7)
68
68
  unicode-display_width (~> 1.0, >= 1.0.1)
69
- ruby-progressbar (1.7.5)
69
+ ruby-progressbar (1.8.0)
70
70
  rubyzip (1.2.0)
71
71
  selenium-webdriver (2.53.0)
72
72
  childprocess (~> 0.5)
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) Jens Finkhaeuser (http://finkhaeuser.de/) and other unobtainum
1
+ Copyright (c) Jens Finkhaeuser (http://finkhaeuser.de/) and other unobtainium
2
2
  contributors. All rights not covered below are reserved.
3
3
 
4
4
  The MIT +no-false-attribs License (MITNFA)
data/README.md CHANGED
@@ -24,6 +24,8 @@ also added.
24
24
  You can use unobtainium on its own, or use it as part of a
25
25
  [cucumber](https://cucumber.io/) test suite.
26
26
 
27
+ [![Unobtainium Demonstration](http://img.youtube.com/vi/82pYWG5uTnM/0.jpg)](http://www.youtube.com/watch?v=82pYWG5uTnM)
28
+
27
29
  Unobtainium's functionality is in standalone classes, but it's all combined in
28
30
  the `Unobtainium::World` module.
29
31
 
@@ -36,7 +38,7 @@ the `Unobtainium::World` module.
36
38
 
37
39
  - The `Config` class is a `PathedHash`, but also reads JSON or YAML files to
38
40
  initialize itself with values. See the documentation on [configuration features](docs/CONFIGURATION.md)
39
- for details
41
+ for details.
40
42
  - The `Runtime` class is a singleton and a `Hash`-like container (but simpler),
41
43
  that destroys all of its contents at the end of a script, calling custom
42
44
  destructors if required. That allows for clean teardown and avoids everything
@@ -74,6 +76,13 @@ The configuration file knows two configuration variables:
74
76
  options hash you might otherwise pass to `Driver.create` as the second
75
77
  parameter.
76
78
 
79
+ See the documentation on [configuration features](docs/CONFIGURATION.md) for
80
+ details.
81
+
82
+ ## Development
83
+
84
+ - [driver development](docs/DRIVERS.md)
85
+
77
86
  # Credits
78
87
  This gem is inspired by [LapisLazuli](https://github.com/spriteCloud/lapis-lazuli),
79
88
  but vastly less complex, and aims to stay so.
@@ -170,7 +170,9 @@ drivers:
170
170
  The tests can be run on device/browser farms. Typically you only need to
171
171
  configure drivers, much like for mobile testing. The following example
172
172
  is for [TestingBot](https://testingbot.com). Note that each farm expects
173
- different configuration keys for selecting browsers and for authentication.
173
+ different configuration keys for selecting browsers and for authentication,
174
+ and that `unobtainium` just passes these values through to Selenium and/or
175
+ Appium.
174
176
 
175
177
  ```yaml
176
178
  # config/config.yml
data/docs/DRIVERS.md ADDED
@@ -0,0 +1,102 @@
1
+ # Drivers
2
+
3
+ Drivers are used to access web pages and their content. Since unobtainium is
4
+ designed to be primarily a wrapper for Selenium and Appium, the built-in
5
+ drivers provide roughly the same API, but there is no strict requirement for
6
+ this.
7
+
8
+ Driver implementations are classes which are required to have a number of
9
+ class methods, described below. No requirements on instance methods are made.
10
+
11
+ # Required Class Methods
12
+
13
+ The class methods required by unobtainium are as follows:
14
+
15
+ - `matches?` accepting a String or Symbol label, as passed by the user to
16
+ `Driver#create`. Must return true if the driver implementation matches this
17
+ label, i.e. if this driver implementation is to be used when the user
18
+ specifies this particular label.
19
+ Note that multiple driver implementations can match the same label; the order
20
+ of preference is an implementation detail.
21
+ - *[optional]* `resolve_options` accepting the label and options passed to
22
+ `Driver#create` by the user (defaulting to an empty hash). The function must
23
+ return the label and options again, however should normalize the label (see
24
+ below) and supplement any options with default values, etc.
25
+ - `ensure_preconditions` accepting the label, and options.
26
+ The method should be used to require any necessary code, and raise errors if
27
+ any other preconditions are not met. See the section on dynamic loading as
28
+ well.
29
+ - Finally, `create` accepting the label and options should return an instance
30
+ of a driver class matching both parameters.
31
+
32
+ # Optional Instance Methods
33
+
34
+ - `destroy` gets invoked by `Runtime` at exit, if `World#driver` is used to
35
+ create the instance. It can be used to tear down the driver instance cleanly.
36
+
37
+ # Label Normalization & Configuration Resolution
38
+
39
+ The built-in drivers respond to many different labels, some of which are
40
+ aliases for a *normalized* label. For example, you can specify `:headless`,
41
+ which is an alias for `:phantomjs`.
42
+
43
+ Label normalization should return `:phantomjs` in the example above.
44
+
45
+ The main goal behind configuration resolution is to provide a way for driver
46
+ implementations to translate configuration keys into a form better suited to
47
+ the implementation.
48
+
49
+ For example, the Selenium driver symbolizes keys, because `selenium-webdriver`
50
+ requires symbol keys. On the other hand, the [configuration](./CONFIGURATION.md)
51
+ system produces String keys only.
52
+
53
+ But you can use this step also to expand shortcut options. The Appium implentation
54
+ allows you to more simply specify some mobile browsers, expanding this into
55
+ capabilities required by Appium itself.
56
+
57
+ # Instance Management
58
+
59
+ The `World#driver` function registers driver implementations with `Runtime` to
60
+ be destroyed at exit.
61
+
62
+ In order to allow multiple driver instances for e.g. multi-browser testing, but
63
+ simultaneously manage instances as described above, the normalized label and
64
+ resolved configuration (see above) are used to generate unique keys.
65
+
66
+ The basic principle is that `World#driver` will be called multiple times in a
67
+ test suite. If it is invoked twice with the same parameters, the same instance
68
+ should be returned. Parameterless invocations should always return the same
69
+ instance, as defined by the configuration. For the pattern inclined reader, this
70
+ is an implementation of the [flyweight pattern](https://en.wikipedia.org/wiki/Flyweight_pattern).
71
+
72
+ Therefore, `resolve_options` should return identical results for two invocations
73
+ with *semantically* identical input.
74
+
75
+ # Dynamic Loading
76
+
77
+ In order not to create hard dependencies in unobtainium on specific versions of
78
+ Selenium, Appium and PhantomJS, these dependencies are only required when
79
+ `ensure_preconditions` is being invoked. That lets users decide which versions
80
+ to require, and skip libraries they do not use.
81
+
82
+ Your driver implementation does not have to follow the same pattern, unless you
83
+ want to see it merged into unobtainium itself.
84
+
85
+ # Registering an Implementation
86
+
87
+ When you have written your class to conform to the above API, all that is left
88
+ to do is to register it with unobtainium:
89
+
90
+ ```ruby
91
+ class MyDriver
92
+ # implementatin
93
+ end # class MyDriver
94
+
95
+ ::Unobtainium::Driver.register_implementation(MyDriver, __FILE__)
96
+ ```
97
+
98
+ The second path parameter should always be set to `__FILE__`. It is used to
99
+ ensure that if your library is included multiple times, the driver does not
100
+ get registered more than once. On the other hand, a different implementation
101
+ with the same class name would raise an error when `register_implementation`
102
+ is invoked.
@@ -27,6 +27,17 @@ module Unobtainium
27
27
  class PathedHash
28
28
  include RecursiveMerge
29
29
 
30
+ DEFAULT_PROC = proc do |hash, key|
31
+ case key
32
+ when String
33
+ sym = key.to_sym
34
+ hash[sym] if hash.key?(sym)
35
+ when Symbol
36
+ str = key.to_s
37
+ hash[str] if hash.key?(str)
38
+ end
39
+ end.freeze
40
+
30
41
  ##
31
42
  # Initializer. Accepts `nil`, hashes or pathed hashes.
32
43
  #
@@ -38,6 +49,8 @@ module Unobtainium
38
49
  @data = init.dup
39
50
  end
40
51
  @separator = '.'
52
+
53
+ @data.default_proc = DEFAULT_PROC
41
54
  end
42
55
 
43
56
  # @return [String] the separator is the character or pattern splitting paths.
@@ -106,6 +119,7 @@ module Unobtainium
106
119
  # For write methods, we need to create intermediary hashes.
107
120
  leaf = recursive_fetch(components, @data,
108
121
  create: WRITE_METHODS.include?(method))
122
+ leaf.default_proc = DEFAULT_PROC
109
123
 
110
124
  # If we have a leaf, we want to send the requested method to that
111
125
  # leaf.
@@ -8,5 +8,5 @@
8
8
  #
9
9
  module Unobtainium
10
10
  # The current release version
11
- VERSION = "0.3.0".freeze
11
+ VERSION = "0.3.1".freeze
12
12
  end
@@ -36,7 +36,11 @@ module Unobtainium
36
36
  # Return the global configuration, loaded from `World#config_file`
37
37
  def config
38
38
  return ::Unobtainium::Runtime.instance.store_with_if(:config) do
39
- ::Unobtainium::Config.load_config(::Unobtainium::World.config_file)
39
+ begin
40
+ ::Unobtainium::Config.load_config(::Unobtainium::World.config_file)
41
+ rescue Errno::ENOENT
42
+ {}
43
+ end
40
44
  end
41
45
  end
42
46
 
@@ -54,6 +54,31 @@ describe ::Unobtainium::PathedHash do
54
54
  expect(ph["bar.nope"]).to eql nil
55
55
  end
56
56
 
57
+ it "can be used with indifferent access from string key" do
58
+ sample = {
59
+ "foo" => 42,
60
+ }
61
+ ph = ::Unobtainium::PathedHash.new(sample)
62
+
63
+ expect(ph["foo"]).to eql 42
64
+ expect(ph[:foo]).to eql 42
65
+ end
66
+
67
+ it "can be used with indifferent access from symbol key" do
68
+ sample = {
69
+ foo: 42,
70
+ bar: {
71
+ baz: 'quux',
72
+ }
73
+ }
74
+ ph = ::Unobtainium::PathedHash.new(sample)
75
+
76
+ expect(ph["foo"]).to eql 42
77
+ expect(ph[:foo]).to eql 42
78
+
79
+ expect(ph['bar.baz']).to eql 'quux'
80
+ end
81
+
57
82
  it "treats a single separator as the root" do
58
83
  sample = { "foo" => 42 }
59
84
  ph = ::Unobtainium::PathedHash.new(sample)
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,11 @@
1
+ # Only start CodeClimate from travis
2
+ if ENV['CODECLIMATE_REPO_TOKEN']
3
+ require 'codeclimate-test-reporter'
4
+ CodeClimate::TestReporter.start
5
+ end
6
+
7
+ # Always start SimpleCov
1
8
  require 'simplecov'
2
9
  SimpleCov.start do
3
10
  add_filter 'unobtainium/drivers'
4
11
  end
5
-
6
- require "codeclimate-test-reporter"
7
- CodeClimate::TestReporter.start
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.0
4
+ version: 0.3.1
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-19 00:00:00.000000000 Z
11
+ date: 2016-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -185,6 +185,7 @@ files:
185
185
  - Rakefile
186
186
  - config/config.yml
187
187
  - docs/CONFIGURATION.md
188
+ - docs/DRIVERS.md
188
189
  - features/step_definitions/steps.rb
189
190
  - features/support/env.rb
190
191
  - features/world.feature