with_clues 1.1.0 → 1.2.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
  SHA256:
3
- metadata.gz: 2e8a1a2ede55c2de31c9a7a7e53a576b4a52dd08001b87a9406e8b9a16fa5495
4
- data.tar.gz: 7e75e12ebe1d192c74813521f4800d2e70dd2641e1c8e70f5cda563fea700100
3
+ metadata.gz: 270e2b6f2af29d8d7871cba1103db16ec84efd97380acb4592b094729adfb994
4
+ data.tar.gz: 22de0039993571e0a491cbe9ec5d1b53021d184793d99bfc47ffb34d64ca833a
5
5
  SHA512:
6
- metadata.gz: 2c3722af382010534c4020a70b17ff7c6907c101fc0f6159758193a8152b30850a0b208dcfd6a94d1128275b8026c524bad9a2e4cac55bd292cb4c4bc9dc0645
7
- data.tar.gz: c90b2558520118f6617db83b2a3a48317ee51a03bfd01fc3eec9a726f6e4b5aea34fccc03cae5d74c03aed117eefdf8b39942084f59eed2ecadcb3784954907a
6
+ metadata.gz: f019bd0a5354f762b4a7304d88509054fe53078528c188e8186aa8af134ff40d99042b0c3556ea2c2497317e0d899e764a6649e78f22ed1aea53e5301ee83c6f
7
+ data.tar.gz: d106562538a1cd625d422e1e500aa34564c5f1c97bfeea30fa6083c7e7f1b99d4addf59f7046c29575122f0b43a15c61b7efefc6fba16ef8a0aeec0107b15148
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ See [https://github.com/sustainable-rails/with\_clues/releases](https://github.com/sustainable-rails/with_clues/releases)
data/README.md CHANGED
@@ -107,13 +107,15 @@ There are three clues included:
107
107
  `with_clues` is intended as a diagnostic tool you can develop and enhance over time. As your team writes more code or develops
108
108
  more conventions, you can develop diagnostics as well.
109
109
 
110
- To add one, create a class that implements `dump(notifier, context:)`:
110
+ To add one, create a class that implements `dump(notifier, context:)` or `dump(notifier, context:, page:)`:
111
111
 
112
112
  * `notifier` is a `WithClues::Notifier` that you should use to produce output:
113
113
  * `notify` - output text, preceded with `[ with_clues ]` (this is so you can tell output from your code vs from `with_clues`)
114
114
  * `blank_line` - a blank line (no prefix)
115
115
  * `notify_raw` - output text without a prefix, useful for removing ambiguity about what is being output
116
116
  * `context:` the context passed into `with_clues` (nil if it was omitted)
117
+ * `page:` If `dump` requires this keyword, your clue will only be used in a browser context when the Capybara `page` object is available.
118
+ In that case, that is what is passed in.
117
119
 
118
120
  For example, suppose you want to output information about an Active Record like so:
119
121
 
@@ -153,6 +155,9 @@ WithClues::Method.use_custom_clue ActiveRecordClues
153
155
 
154
156
  You can use multiple clues by repeatedly calling `use_custom_clue`
155
157
 
158
+ Note that if your clue implements the three-arg version of `dump` ( `dump(notifier, context:, page:)` ), it will *only* be used when in
159
+ a context where Capybara's `page` element in in play.
160
+
156
161
  ## Developing
157
162
 
158
163
  * Get set up with `bin/setup`
@@ -6,24 +6,35 @@ module WithClues
6
6
  return
7
7
  end
8
8
  if page.driver.respond_to?(:browser)
9
- if page.driver.browser.respond_to?(:manage)
10
- if page.driver.browser.manage.respond_to?(:logs)
11
- logs = page.driver.browser.manage.logs
12
- browser_logs = logs.get(:browser)
13
- notifier.notify "BROWSER LOGS {"
14
- browser_logs.each do |log|
15
- notifier.notify_raw log.message
16
- end
17
- notifier.notify "} END BROWSER LOGS"
18
- else
19
- notifier.notify "NO BROWSER LOGS: page.driver.browser.manage #{page.driver.browser.manage.class} does not respond to #logs"
9
+ logs = locate_logs(page.driver.browser, notifier: notifier)
10
+ if !logs.nil?
11
+ browser_logs = logs.get(:browser)
12
+ notifier.notify "BROWSER LOGS {"
13
+ browser_logs.each do |log|
14
+ notifier.notify_raw log.message
20
15
  end
21
- else
22
- notifier.notify "NO BROWSER LOGS: page.driver.browser #{page.driver.browser.class} does not respond to #manage"
16
+ notifier.notify "} END BROWSER LOGS"
23
17
  end
24
18
  else
25
- notifier.notify "NO BROWSER LOGS: page.driver #{page.driver.class} does not respond to #browser"
19
+ notifier.notify "[with_clues: #{self.class}] NO BROWSER LOGS: page.driver #{page.driver.class} does not respond to #browser"
26
20
  end
27
21
  end
22
+
23
+ private
24
+
25
+ def locate_logs(browser, notifier:)
26
+ if browser.respond_to?(:logs)
27
+ return browser.logs
28
+ elsif browser.respond_to?(:manage)
29
+ if browser.manage.respond_to?(:logs)
30
+ return browser.manage.logs
31
+ end
32
+ notifier.notify "[with_clues: #{self.class}] NO BROWSER LOGS: page.driver.browser.manage #{browser.manage.class} does not respond to #logs"
33
+ else
34
+ notifier.notify "[with_clues: #{self.class}] NO BROWSER LOGS: page.driver.browser #{browser.class} does not respond to #manage or #logs"
35
+ end
36
+ nil
37
+ end
38
+
28
39
  end
29
40
  end
@@ -1,6 +1,7 @@
1
1
  require_relative "html"
2
2
  require_relative "browser_logs"
3
3
  require_relative "notifier"
4
+ require_relative "private/custom_clue_method_analysis"
4
5
 
5
6
  module WithClues
6
7
  module Method
@@ -23,18 +24,25 @@ module WithClues
23
24
  @@clue_classes[:custom].each do |klass|
24
25
  klass.new.dump(notifier, context: context)
25
26
  end
26
- if !defined?(page)
27
- raise ex
28
- end
29
- notifier.notify "Test failed: #{ex.message}"
30
- @@clue_classes[:require_page].each do |klass|
31
- klass.new.dump(notifier, context: context, page: page)
27
+ if defined?(page)
28
+ notifier.notify "Test failed: #{ex.message}"
29
+ @@clue_classes[:require_page].each do |klass|
30
+ klass.new.dump(notifier, context: context, page: page)
31
+ end
32
32
  end
33
33
  raise ex
34
34
  end
35
35
 
36
36
  def self.use_custom_clue(klass)
37
- @@clue_classes[:custom] << klass
37
+ dump_method = klass.instance_method(:dump)
38
+ analysis = WithClues::Private::CustomClueMethodAnalysis.from_method(dump_method)
39
+ if analysis.standard_implementation?
40
+ @@clue_classes[:custom] << klass
41
+ elsif analysis.requires_page_object?
42
+ @@clue_classes[:require_page] << klass
43
+ else
44
+ analysis.raise_exception!
45
+ end
38
46
  end
39
47
  end
40
48
  end
@@ -0,0 +1,139 @@
1
+ module WithClues
2
+ module Private
3
+ class CustomClueMethodAnalysis
4
+
5
+ def self.from_method(unbound_method)
6
+
7
+ params = unbound_method.parameters.map { |param_array| Param.new(param_array) }
8
+
9
+ if params.size == 2
10
+ two_arg_method = TwoArgMethod.new(params)
11
+ if two_arg_method.valid?
12
+ return StandardImplementation.new
13
+ end
14
+
15
+ return BadParams.new(two_arg_method.errors)
16
+
17
+ elsif params.size == 3
18
+ three_arg_method = ThreeArgMethod.new(params)
19
+ if three_arg_method.valid?
20
+ return RequiresPageObject.new
21
+ end
22
+ return BadParams.new(three_arg_method.errors)
23
+ end
24
+
25
+ BadParams.new([])
26
+ end
27
+
28
+ def standard_implementation?
29
+ false
30
+ end
31
+
32
+ def requires_page_object?
33
+ false
34
+ end
35
+
36
+ def raise_exception!
37
+ raise StandardError.new("Unimplemented condition found inside #from_method")
38
+ end
39
+
40
+ class Param
41
+
42
+ def initialize(method_param_array)
43
+ @type = method_param_array[0]
44
+ @name = method_param_array[1]
45
+
46
+ end
47
+
48
+ def required?
49
+ @type == :req
50
+ end
51
+ def keyword_required?
52
+ @type == :keyreq
53
+ end
54
+
55
+ def named?(*allowed_names)
56
+ allowed_names.include?(@name)
57
+ end
58
+ def name
59
+ if self.keyword_required?
60
+ "#{@name}:"
61
+ else
62
+ @name
63
+ end
64
+ end
65
+ end
66
+
67
+ class TwoArgMethod
68
+ attr_reader :errors
69
+ def initialize(params)
70
+ @errors = []
71
+ if !params[0].required?
72
+ @errors << "Param 1, #{params[0].name}, is not required"
73
+ end
74
+ require_keyword(2,params[1])
75
+ end
76
+
77
+ def valid?
78
+ @errors.empty?
79
+ end
80
+ private
81
+
82
+ def require_keyword(param_number, param)
83
+ if !param.keyword_required?
84
+ @errors << "Param #{param_number}, #{param.name}, is not a required keyword param"
85
+ end
86
+ if !param.named?(*allowed_names)
87
+ @errors << "Param #{param_number}, #{param.name}, should be named context:"
88
+ end
89
+ end
90
+
91
+ def allowed_names
92
+ [ :context ]
93
+ end
94
+ end
95
+
96
+ class ThreeArgMethod < TwoArgMethod
97
+ def initialize(params)
98
+ super(params)
99
+ require_keyword(3,params[2])
100
+ end
101
+ private
102
+ def allowed_names
103
+ [ :context, :page ]
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ class GoodParams < CustomClueMethodAnalysis
110
+ def raise_exception!
111
+ raise StandardError.new("You should not have called .exception on a #{self.class.name}")
112
+ end
113
+ end
114
+
115
+ class RequiresPageObject < CustomClueMethodAnalysis
116
+ def requires_page_object?
117
+ true
118
+ end
119
+ end
120
+
121
+ class StandardImplementation < CustomClueMethodAnalysis
122
+ def standard_implementation?
123
+ true
124
+ end
125
+ end
126
+
127
+ class BadParams < CustomClueMethodAnalysis
128
+ def initialize(errors)
129
+ @message = errors.empty? ? DEFAULT_ERROR : errors.join(", ")
130
+ end
131
+
132
+ DEFAULT_ERROR = "dump must take one required param, one keyword param named context: and an optional keyword param named page:"
133
+
134
+ def raise_exception!
135
+ raise NameError.new(@message)
136
+ end
137
+ end
138
+ end
139
+ end
@@ -1,3 +1,3 @@
1
1
  module WithClues
2
- VERSION="1.1.0"
2
+ VERSION="1.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: with_clues
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Copeland
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-04 00:00:00.000000000 Z
11
+ date: 2022-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -77,6 +77,7 @@ files:
77
77
  - ".gitignore"
78
78
  - ".rspec"
79
79
  - ".tool-versions"
80
+ - CHANGELOG.md
80
81
  - CODE_OF_CONDUCT.md
81
82
  - CONTRIBUTING.md
82
83
  - Gemfile
@@ -95,6 +96,7 @@ files:
95
96
  - lib/with_clues/html.rb
96
97
  - lib/with_clues/method.rb
97
98
  - lib/with_clues/notifier.rb
99
+ - lib/with_clues/private/custom_clue_method_analysis.rb
98
100
  - lib/with_clues/version.rb
99
101
  - with_clues.gemspec
100
102
  homepage: https://sustainable-rails.com