edsl 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9f097097fb3758a17ebdf2773fad9a81db56ae733288676e8e8ed04ea53cdd9
4
- data.tar.gz: e385e0e3eb350e6bc4e5f61f8492408b92fdfd071196be2c667d2393e41a8f12
3
+ metadata.gz: 6436be2a1cda4e3ab53243cfddb9b19563530c5c8b55afe918562434c168b5d4
4
+ data.tar.gz: 78067b244a23b49e6401a68766551d6f35972e09e798d972bf0d7ca467db7957
5
5
  SHA512:
6
- metadata.gz: 326e347590d35861c9a2470a774bb0e24287578990907a9770d34c0e993f38461cfeee07249303e67a6894eb76a02a04e7af06249433eb6cb176c645e7f68d4e
7
- data.tar.gz: a5ce2cc40b44e3d2a3fff8dcdff72b3737f41087056cf7d79b4ed484e07c69650f5a4c1959f82710e81cf0a39ff7e2b5fac2459583b60a14765237057efd768e
6
+ metadata.gz: 65efa208961743a6a4da88502620b197bf7fc60eb5abe20bb23418bc398e2b9d5b9259122590f8083fea4a1a59af9a5e5dcc90f7c7d07d6feabf1812394ae40b
7
+ data.tar.gz: 46555775d32e1ecfa8bb79f5cfaf3d83937b98291027053ea8d1d4b19fd0dc4cebc6e4393886359f58c0a4ad7711fe9fe2c4d3dcad89baf3e37f71a0c3ecef69
data/.gitignore CHANGED
@@ -7,6 +7,8 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  /.idea/
10
+ *.gem
11
+ scratch.rb
10
12
 
11
13
  # rspec failure tracking
12
14
  .rspec_status
data/.rubocop.yml CHANGED
@@ -1,4 +1,9 @@
1
- # Offense count: 1
1
+ AllCops:
2
+ Exclude:
3
+ - 'spec/*'
4
+ - 'scratch.rb'
5
+ - '*.gemspec'
6
+
2
7
  Lint/Debugger:
3
8
  Exclude:
4
9
  - 'bin/console'
data/README.md CHANGED
@@ -1,13 +1,132 @@
1
- # EDSL
1
+ # EDSL - Element DSL
2
2
 
3
- This gem implements an extensible DSL for implementing the page object, pattern.
4
- It is very similar to PageObject and can almost be used as a drop-in replacement for it.
5
- However, this gem focuses the element DSL portion only.
3
+ This gem implements an extensible DSL for declaring web elements as part of a page object pattern. This gem does **not** implement the page object pattern, for an implementation of page object using this gem see edsl-pageobject.
6
4
 
7
- This gem was created to simplify the process of creating abstractions for web automation.
8
- Because EDSL is easy to extend, you can create custom DSL methods that are first class citizens
5
+ This gem was created out of the need to rapidly produce abstractions for non-standard web elements. With EDSL support for new accessors and other DSL methods can be implemented without needing to modify any EDSL source or monkey patching. It allows you to rapidly prototype using lambdas, or to build full classes to use in place of existing elements.
6
+
7
+
8
+ ## Accessors
9
+ The DSL includes accessors for all Watir elements and makes it easy to add custom accessors. The accessors for Watir elements are not hand crafted ruby code, they're generated by code that extends EDSL when `edsl/watir_elements` is included.
10
+
11
+ Below are the two lines of code that implement the support for buttons and links:
12
+
13
+ ```ruby
14
+ CLICKABLE_ELEMENTS = %i[button a link].freeze
15
+ CLICKABLE_ELEMENTS.each { |tag| EDSL.define_accessor(tag, how: tag, default_method: :click) }
16
+ ```
17
+
18
+ `EDSL.define_accessor` is the quickest way to add a new accessor. With it you provide a name for your accessor (here we're just using the name of the tag as the name of the accessor) as well as the _default_ set of options to be used in the call to `element`.
19
+
20
+ Most accessors can be implemented as a custom call to `element` in the DSL. Depending on the options passed to `element` you can retrieve any in the DOM. Filling out all those options for every element you wanted to locate would be tedious, so `define_accessor` will define a new method that takes the default options provided, merges them with the ones provided by the developer using the new accessor and then calls `element`.
21
+
22
+ The options hash for `element` can contain the following keys:
23
+
24
+ * __how__ - The method to call, or a proc to call to locate this element. i.e. `:div`
25
+ * __default_method__ - The method to call when `name` is called. If not provided it will be the same as calling `name_element`
26
+ * __assign_method__ - The method to call when `name=` is called. If not set the `name=` method will not be created.
27
+ * __presence_method__ - The method to call when `name?` is called. If not provided it will default to `:present?`
28
+ * __hooks__ - CptHook::HookDefintions to apply to the element before returning it in `name_element`. If not supplied, hooks are not applied.
29
+ * __wrapper_fn__ - Optional, can be set to a proc that will be called with the Watir element and it's parent container as arguments.
30
+
31
+ __Anything left in the hash after edsl options are deleted are passed to :how__
32
+
33
+ These options allow for several possibilities, for example we could create a new `span_button` for spans that are clickable like buttons with:
34
+
35
+ ```ruby
36
+ EDSL.define_accessor(:span_button, how: span, default_method: :click)
37
+
38
+ ```
39
+
40
+ For more complex needs one can use the `EDSL.extend_dsl` method and define new DSL methods directly. Here's an example from edsl-pageobject where an additional parameter was needed.
41
+
42
+ ```ruby
43
+ EDSL.extend_dsl do
44
+ def section(name, section_class, opts)
45
+ element(name, { how: :div, assign_method: :populate_with,
46
+ wrapper_fn: lambda { |element, _container|section_class.new(element, self) } }.merge(opts))
47
+ end
48
+ end
49
+ ```
50
+
51
+ This makes use of the `:wrapper_fn` option to create a new instance of the provided section class with the actual element as it's root and return that when asked for `name` or `name_element`.
52
+
53
+ ## Consuming accessors
54
+ In order to make use of accessors you need to include the EDSL module into a class. EDSL assumes that the class including it respond to any of the methods provided in the `:how` option. The easy way to do that is to inherit from `EDSL::ElementContainer`. The key piece of "magic" in it is that it inherits from `SimpleDelegator` and can be initialized with a Watir element or browser.
55
+
56
+ Most accessors have a signature of `accessor(name, opts)` where `name` determines the method names generated and `opts` provide options. At a minimum `opts` should include the locators for the element. For Watir accessors the options are the same as the corresponding Watir calls.
57
+
58
+ #### First a simple example using one of the Watir accessors
59
+
60
+ ```ruby
61
+ class LoginPage < EDSL::ElementContainer
62
+ text_field(:username, id: 'user_name')
63
+ end
64
+
65
+ # Now we could do:
66
+ page = LoginPage.new(browser)
67
+
68
+ page.username? # See if the text field exists
69
+ page.username = 'zerocool` # Set the username
70
+ name = page.username # Get the username
71
+ username_edit = page.username_element # The the underlying Watir element
72
+ ```
73
+
74
+ #### Due to the way DSL extensions work, it is possible to override behavior at the top level.
75
+
76
+ For example, let's say for some link what we care about is where it points, not visiting it. The default method for a link would be to call `click` forcing us to use `page.link_element.href` to get what we're after when a more `page.link` or `page.link_href` would make it easier to compare with values from a fixture. We can accomplish this when we declare the link like so:
77
+
78
+ ```ruby
79
+ link(:result_link, index: 0, default_method: :href)
80
+ ```
81
+
82
+ #### We can even find our own elements
83
+ By supplying a block to any accessor that block will be called to locate the element.
84
+
85
+ ```ruby
86
+ link(:result_link, default_method: :href) { some_variable? : link(index: 0) ? link(index: 1) }
87
+ ```
88
+
89
+ #### We can customize value types
90
+ Let's say we had a text field for entering dates we want to be able to take advantage of Chronic and make our lives easier. We can define a couple lambda functions provide those in the options.
91
+
92
+ ```ruby
93
+ # Return a date, either by parsing a string or returning the passed value back
94
+ v_to_d = lambda { |val| val.is_a?(String) ? Chronic.parse(val) : val }
95
+
96
+ # Set an element based on a date, or a string Chronic can parse to a date
97
+ DATE_FORMAT = '%m/%d/%Y'.freeze # Americans ammiright?
98
+ chronic_set = lambda { |name, container, value| container.send("#{name}_element").set(v_to_d.call(value).strftime(DATE_FORMAT)) }
99
+
100
+ # Return a date from the value in the element
101
+ chronic_get = lambda { |name, container| Chronic.parse(container.send("#{name}_element").value) }
102
+
103
+ text_field(:date_input, id: 'the_date', assign_method: chronic_set, default_method: chronic_get )
104
+
105
+ # Now we can do fun things like
106
+ page.date_input = 'next Tuesday'
107
+ ```
108
+
109
+ Another way we could accomplish the same task is to provide a wrapper function that wraps the element in a decorator to modify the behavior like so:
110
+
111
+ ```ruby
112
+ class DateEdit < SimpleDelegatopr
113
+ def value
114
+ Chronic.parse(super)
115
+ end
116
+
117
+ def set(val)
118
+ super(val_to_date(val).strftime(DATE_FORMAT))
119
+ end
120
+
121
+ def val_to_date(val)
122
+ val.is_a?(String) ? Chronic.parse(val) : val
123
+ end
124
+ end
125
+
126
+ text_field(:date_input, id: 'the_date',
127
+ wrapper_fn: lambda { |ele, _parent| DateEdit.new(ele) } )
128
+ ```
9
129
 
10
- This is still very much a work in progress. Primary documentation can be found in code comments for now.
11
130
 
12
131
  ## Installation
13
132
 
@@ -25,9 +144,6 @@ Or install it yourself as:
25
144
 
26
145
  $ gem install edsl
27
146
 
28
- ## Usage
29
-
30
- TODO: Write usage instructions here
31
147
 
32
148
  ## Development
33
149
 
data/edsl.gemspec CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ['Donavan Stanley']
9
9
  spec.email = ['donavan.stanley@gmail.com']
10
10
 
11
- spec.summary = 'An easy to extend DSL for web elements'
12
- spec.description = 'Page object pattern without PageObject'
13
- spec.homepage = 'https://github.com/jdonavan'
11
+ spec.summary = 'An easy to extend DSL for web elements.'
12
+ spec.description = 'This gem serves as a base for implementing the page object pattern.'
13
+ spec.homepage = 'https://github.com/donavan/edsl'
14
14
  spec.license = 'MIT'
15
15
 
16
16
  spec.metadata['homepage_uri'] = spec.homepage
@@ -28,11 +28,13 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'bundler', '~> 1.16'
29
29
  spec.add_development_dependency 'pry'
30
30
  spec.add_development_dependency 'pry-byebug'
31
+ spec.add_development_dependency 'pry-stack_explorer'
31
32
  spec.add_development_dependency 'rake', '~> 10.0'
32
33
  spec.add_development_dependency 'rspec', '~> 3.0'
33
34
  spec.add_development_dependency 'rubocop'
35
+ spec.add_development_dependency 'simplecov'
34
36
 
35
- spec.add_runtime_dependency 'watir'
36
- spec.add_runtime_dependency 'cpt_hook'
37
- spec.add_runtime_dependency 'facets'
37
+ spec.add_runtime_dependency 'cpt_hook', '~> 0.3'
38
+ spec.add_runtime_dependency 'facets', '~> 3.1'
39
+ spec.add_runtime_dependency 'watir', '~> 6.14'
38
40
  end
data/lib/edsl/dsl.rb CHANGED
@@ -1,26 +1,43 @@
1
1
  # Root module for the gem
2
2
  require 'cpt_hook'
3
3
 
4
+ # Top level module for the gem
4
5
  module EDSL
6
+ # rubocop:disable Metrics/AbcSize
7
+
5
8
  # Ensure the DSL methods are applied when we're included
6
9
  def self.included(cls)
7
10
  cls.extend EDSL::DSL
8
11
 
9
- cls.send(:define_method, :resolve_with) do |with_var|
10
- return self if with_var == :page
11
- return :self if with_var == :element
12
- with_var
13
- end
12
+ add_resolvers(cls)
14
13
 
15
14
  cls.send(:define_method, :apply_hooks) do |hook_defs, element|
16
15
  return nil if element.nil?
17
16
  return element if hook_defs.nil?
17
+
18
18
  hd = hook_defs.dup
19
19
  hd.hooks.each { |hook| hook.call_chain.each { |cc| cc.resolve_with { |c| resolve_with(c) } } }
20
- hd.hooks.each { |hook| hook.call_chain.each { |cc| cc.resolve_contexts { |c| resolve_with(c) } } }
20
+ hd.hooks.each { |hook| hook.call_chain.each { |cc| cc.resolve_contexts { |c| resolve_context(c) } } }
21
21
  CptHook::Hookable.new(element, hd, self)
22
22
  end
23
23
  end
24
+ # rubocop:enable Metrics/AbcSize
25
+
26
+ def self.add_resolvers(cls)
27
+ cls.send(:define_method, :resolve_with) do |with_var|
28
+ return with_var.call if with_var.is_a?(Proc)
29
+ return self if with_var == :parent
30
+ return :self if with_var == :element
31
+
32
+ with_var
33
+ end
34
+
35
+ cls.send(:define_method, :resolve_context) do |context|
36
+ return context.call if context.is_a?(Proc)
37
+
38
+ context
39
+ end
40
+ end
24
41
 
25
42
  # Use a block to add new methods to the DSL.
26
43
  def self.extend_dsl(&block)
@@ -33,6 +50,16 @@ module EDSL
33
50
  DSL.define_accessor(acc_name, default_opts)
34
51
  end
35
52
 
53
+ # Allow an accessor to be accessed by a different name
54
+ def self.alias_accessor(new_name, acc_name)
55
+ DSL.alias_accessor(new_name, acc_name)
56
+ end
57
+
58
+ # Allow multiple accessors to be defined at once
59
+ def self.define_accessors(accessor_array)
60
+ DSL.define_accessors(accessor_array)
61
+ end
62
+
36
63
  # These methods will be extended into any class which includes EDSL.
37
64
  # They provide a mechanism for declaring HTML elements as properties of another object
38
65
  module DSL
@@ -53,37 +80,59 @@ module EDSL
53
80
  # element(:username, id: 'some_id', how: Proc.new { |name, container, opts| container.text_field(opts) }, default_method: :value, assign_method: :set)
54
81
  #
55
82
  def element(name, opts)
56
- how = opts.delete(:how)
57
- default_method = opts.delete(:default_method)
83
+ element_method = _add_element_method(name, opts)
84
+ _add_common_methods(name, element_method, opts)
85
+ _add_assignment_methods(name, element_method, opts)
86
+ end
87
+
88
+ # Helper function to reduce perceived complexity of element
89
+ def _add_assignment_methods(name, element_method, opts)
58
90
  assign_method = opts.delete(:assign_method)
59
- presence_method = opts.delete(:presence_method) || :present?
60
- hooks = opts.delete(:hooks)
61
- wrapper_fn = opts.delete(:wrapper_fn) || lambda { |_e, _p| return _e }
91
+ return unless assign_method
62
92
 
63
- ele_meth = "#{name}_element"
64
- define_method(ele_meth) do
65
- ele = yield name, self, opts if block_given?
66
- ele ||= how.call(name, self, opts) if how.is_a?(Proc)
67
- ele ||= send(how, opts)
68
- ele = wrapper_fn.call(ele, self)
69
- hooks.nil? ? ele : send(:apply_hooks, hooks, ele)
93
+ define_method("#{name}=") do |value|
94
+ return assign_method.call(name, self, value) if assign_method.is_a?(Proc)
95
+
96
+ send(element_method).send(assign_method, value)
70
97
  end
98
+ end
99
+
100
+ # Helper function to reduce perceived complexity of element
101
+ def _add_common_methods(name, element_method, opts)
102
+ default_method = opts.delete(:default_method)
103
+ presence_method = opts.delete(:presence_method) || :present?
71
104
 
72
105
  define_method(name) do
73
106
  return default_method.call(name, self) if default_method.is_a?(Proc)
74
- default_method.nil? ? send(ele_meth) : send(ele_meth).send(default_method)
107
+
108
+ default_method.nil? ? send(element_method) : send(element_method).send(default_method)
75
109
  end
76
110
 
77
111
  define_method("#{name}?") do
78
- send(ele_meth).send(presence_method)
112
+ return presence_method.call(name, self) if presence_method.is_a?(Proc)
113
+
114
+ send(element_method).send(presence_method)
79
115
  end
116
+ end
80
117
 
81
- return unless assign_method
118
+ # rubocop:disable Metrics/AbcSize
119
+ # Helper function to reduce perceived complexity of element
120
+ def _add_element_method(name, opts)
121
+ how = opts.delete(:how)
122
+ hooks = opts.delete(:hooks)
123
+ wrapper_fn = opts.delete(:wrapper_fn) || ->(e, _p) { return e }
82
124
 
83
- define_method("#{name}=") do |value|
84
- send(ele_meth).send(assign_method, value)
125
+ ele_meth = "#{name}_element"
126
+ define_method(ele_meth) do
127
+ ele = yield if block_given?
128
+ ele ||= how.call(name, self, opts) if how.is_a?(Proc)
129
+ ele ||= send(how, opts)
130
+ ele = wrapper_fn.call(ele, self)
131
+ hooks.nil? ? ele : send(:apply_hooks, hooks, ele)
85
132
  end
133
+ ele_meth
86
134
  end
135
+ # rubocop:enable Metrics/AbcSize
87
136
 
88
137
  # This vastly simplifies defining custom accessors.
89
138
  # If we had to use the base element method for every field that would be tedious.
@@ -100,10 +149,12 @@ module EDSL
100
149
  end
101
150
  end
102
151
 
152
+ # Allow an accessor to be accessed by a different name
103
153
  def self.alias_accessor(new_name, acc_name)
104
154
  self.class.send(:alias_method, new_name, acc_name)
105
155
  end
106
156
 
157
+ # Allow multiple accessors to be defined at once
107
158
  def self.define_accessors(accessor_array)
108
159
  accessor_array.each { |acc| define_accessor(*acc) }
109
160
  end
@@ -14,7 +14,7 @@ module EDSL
14
14
  include EDSL
15
15
 
16
16
  attr_reader :parent_container
17
- alias_method :root_element, :__getobj__
17
+ alias root_element __getobj__
18
18
 
19
19
  def initialize(element, parent = nil)
20
20
  super(element)
data/lib/edsl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module EDSL
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -1,16 +1,19 @@
1
+ require 'facets/string/snakecase'
2
+
1
3
  module EDSL
2
4
  # This module extends the DSL to include the various Watir elements
3
5
  module WatirElements
4
6
  SPECIAL_ELEMENTS = %i[button a radio_set input select textarea].freeze
5
7
 
6
- Watir.tag_to_class.keys.reject { |k| SPECIAL_ELEMENTS.include?(k) }.each do |tag|
8
+ TEXT_ELEMENTS = Watir.tag_to_class.keys.reject { |k| SPECIAL_ELEMENTS.include?(k) }.map { |t| t.to_s.snakecase }.freeze
9
+ TEXT_ELEMENTS.each do |tag|
7
10
  EDSL.define_accessor(tag, how: tag, default_method: :text)
8
11
  end
9
12
 
10
13
  CLICKABLE_ELEMENTS = %i[button a link].freeze
11
14
  CLICKABLE_ELEMENTS.each { |tag| EDSL.define_accessor(tag, how: tag, default_method: :click) }
12
15
 
13
- CONTENT_EDITABLE_ELEMENTS = %i[text_field textarea text_area].freeze
16
+ CONTENT_EDITABLE_ELEMENTS = %i[text_field textarea].freeze
14
17
  CONTENT_EDITABLE_ELEMENTS.each { |tag| EDSL.define_accessor(tag, how: tag, default_method: :value, assign_method: :set) }
15
18
 
16
19
  SETABLE_ELEMENTS = %i[radio checkbox].freeze
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: edsl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Donavan Stanley
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-22 00:00:00.000000000 Z
11
+ date: 2018-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-stack_explorer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -95,13 +109,13 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: watir
112
+ name: simplecov
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - ">="
102
116
  - !ruby/object:Gem::Version
103
117
  version: '0'
104
- type: :runtime
118
+ type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
@@ -112,31 +126,45 @@ dependencies:
112
126
  name: cpt_hook
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - ">="
129
+ - - "~>"
116
130
  - !ruby/object:Gem::Version
117
- version: '0'
131
+ version: '0.3'
118
132
  type: :runtime
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
- - - ">="
136
+ - - "~>"
123
137
  - !ruby/object:Gem::Version
124
- version: '0'
138
+ version: '0.3'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: facets
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
- - - ">="
143
+ - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: '0'
145
+ version: '3.1'
132
146
  type: :runtime
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
- - - ">="
150
+ - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: '0'
139
- description: Page object pattern without PageObject
152
+ version: '3.1'
153
+ - !ruby/object:Gem::Dependency
154
+ name: watir
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '6.14'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '6.14'
167
+ description: This gem serves as a base for implementing the page object pattern.
140
168
  email:
141
169
  - donavan.stanley@gmail.com
142
170
  executables: []
@@ -160,12 +188,12 @@ files:
160
188
  - lib/edsl/element_container.rb
161
189
  - lib/edsl/version.rb
162
190
  - lib/edsl/watir_elements.rb
163
- homepage: https://github.com/jdonavan
191
+ homepage: https://github.com/donavan/edsl
164
192
  licenses:
165
193
  - MIT
166
194
  metadata:
167
- homepage_uri: https://github.com/jdonavan
168
- source_code_uri: https://github.com/jdonavan
195
+ homepage_uri: https://github.com/donavan/edsl
196
+ source_code_uri: https://github.com/donavan/edsl
169
197
  post_install_message:
170
198
  rdoc_options: []
171
199
  require_paths:
@@ -185,5 +213,5 @@ rubyforge_project:
185
213
  rubygems_version: 2.7.7
186
214
  signing_key:
187
215
  specification_version: 4
188
- summary: An easy to extend DSL for web elements
216
+ summary: An easy to extend DSL for web elements.
189
217
  test_files: []