with_clues 1.0.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: a9250314165c2529d8ac1a12ccc085a2abf2098f057b5778b1544c34dff21f9c
4
- data.tar.gz: 9f737b4264d64955b17174ac44c9336d8dfa8fe79152b00cf96447b48ac15208
3
+ metadata.gz: 270e2b6f2af29d8d7871cba1103db16ec84efd97380acb4592b094729adfb994
4
+ data.tar.gz: 22de0039993571e0a491cbe9ec5d1b53021d184793d99bfc47ffb34d64ca833a
5
5
  SHA512:
6
- metadata.gz: 9bf1631b167e606916d8d7e87bba3236bf829c0614c722b05fc5ac99c103cd96fb44f4c1e548bbcdbfcc71205e3661ba9d9d58f0a844cf7c004dc618174ab253
7
- data.tar.gz: cdb5e7836e91a48764ad70afeeed9f1aeaf872b3cf748c4663431e522cfc7f4006a00045ab7a03b7fa905d9865f5442dba5621b0e47172fa3c18b4738d1022fe
6
+ metadata.gz: f019bd0a5354f762b4a7304d88509054fe53078528c188e8186aa8af134ff40d99042b0c3556ea2c2497317e0d899e764a6649e78f22ed1aea53e5301ee83c6f
7
+ data.tar.gz: d106562538a1cd625d422e1e500aa34564c5f1c97bfeea30fa6083c7e7f1b99d4addf59f7046c29575122f0b43a15c61b7efefc6fba16ef8a0aeec0107b15148
data/.circleci/config.yml CHANGED
@@ -1,35 +1,98 @@
1
- version: 2.1
2
- orbs:
3
- # See https://circleci.com/developer/orbs/orb/circleci/ruby
4
- ruby: circleci/ruby@1.1.2
5
- jobs: # keyword
6
- test: # my name for the job
7
- parameters: # keyword
8
- ruby-version: # my parameter name
9
- type: string # type is a keyword
10
- docker: # keyword
11
- - image: cimg/base:stable
12
- steps: # keyword
13
- - checkout # magic name
14
- - ruby/install: # ruby/ is from the orb name, install is a command in that orb
15
- version: << parameters.ruby-version >> # magic nonsense for param subst
16
- - run:
17
- command: "bin/setup"
18
- - run:
19
- name: "Create the test results directory because you can't just store_test_results with a file and if you do you do not get any sort of error because wtf is with this platform?"
20
- command: mkdir -p /tmp/test-results
21
- - run:
22
- command: bin/ci /tmp/test-results/rspec_results.xml
23
- - store_test_results: # store_test_results is magic from circle
24
- path: /tmp/test-results # path is a param to store_test_results and it must be a directory not a file
25
- - store_artifacts: # store_artifacts is magic from circle
26
- path: /tmp/test-results # path is the param to store_artifacts
27
- workflows: # keyword
28
- all-rubies: # my name for the workflow
29
- jobs: # keyword
30
- - test: # my name for the job
31
- matrix: # keyword
32
- parameters: # keyword
33
- # All rubies being maintained per this page:
34
- # https://www.ruby-lang.org/en/downloads/branches/
35
- ruby-version: [ "2.5", "2.6", "2.7", "3.0" ]
1
+ # THIS IS GENERATED - DO NOT EDIT
2
+ # regenerate with bin/mk_circle_config
3
+ # You are very welcome
4
+ ---
5
+ version: '2.1'
6
+ jobs:
7
+ ruby__2_6:
8
+ docker:
9
+ - image: cimg/ruby:2.6
10
+ steps:
11
+ - checkout
12
+ - run:
13
+ name: Setup for build
14
+ command: bin/setup
15
+ - run:
16
+ name: Ensure bin/setup is idempotent
17
+ command: bin/setup
18
+ - run:
19
+ name: Create the test results dir
20
+ command: mkdir -p /tmp/test-results/2.6
21
+ - run:
22
+ name: Run all tests
23
+ command: bin/ci /tmp/test-results/2.6/rspec_results.xml
24
+ - store_test_results:
25
+ path: "/tmp/test-results/2.6"
26
+ - store_artifacts:
27
+ path: "/tmp/test-results/2.6"
28
+ ruby__2_7:
29
+ docker:
30
+ - image: cimg/ruby:2.7
31
+ steps:
32
+ - checkout
33
+ - run:
34
+ name: Setup for build
35
+ command: bin/setup
36
+ - run:
37
+ name: Ensure bin/setup is idempotent
38
+ command: bin/setup
39
+ - run:
40
+ name: Create the test results dir
41
+ command: mkdir -p /tmp/test-results/2.7
42
+ - run:
43
+ name: Run all tests
44
+ command: bin/ci /tmp/test-results/2.7/rspec_results.xml
45
+ - store_test_results:
46
+ path: "/tmp/test-results/2.7"
47
+ - store_artifacts:
48
+ path: "/tmp/test-results/2.7"
49
+ ruby__3_0:
50
+ docker:
51
+ - image: cimg/ruby:3.0
52
+ steps:
53
+ - checkout
54
+ - run:
55
+ name: Setup for build
56
+ command: bin/setup
57
+ - run:
58
+ name: Ensure bin/setup is idempotent
59
+ command: bin/setup
60
+ - run:
61
+ name: Create the test results dir
62
+ command: mkdir -p /tmp/test-results/3.0
63
+ - run:
64
+ name: Run all tests
65
+ command: bin/ci /tmp/test-results/3.0/rspec_results.xml
66
+ - store_test_results:
67
+ path: "/tmp/test-results/3.0"
68
+ - store_artifacts:
69
+ path: "/tmp/test-results/3.0"
70
+ ruby__3_1:
71
+ docker:
72
+ - image: cimg/ruby:3.1
73
+ steps:
74
+ - checkout
75
+ - run:
76
+ name: Setup for build
77
+ command: bin/setup
78
+ - run:
79
+ name: Ensure bin/setup is idempotent
80
+ command: bin/setup
81
+ - run:
82
+ name: Create the test results dir
83
+ command: mkdir -p /tmp/test-results/3.1
84
+ - run:
85
+ name: Run all tests
86
+ command: bin/ci /tmp/test-results/3.1/rspec_results.xml
87
+ - store_test_results:
88
+ path: "/tmp/test-results/3.1"
89
+ - store_artifacts:
90
+ path: "/tmp/test-results/3.1"
91
+ workflows:
92
+ version: 2
93
+ all_rubies:
94
+ jobs:
95
+ - ruby__2_6
96
+ - ruby__2_7
97
+ - ruby__3_0
98
+ - ruby__3_1
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.1.0
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`
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "yaml"
4
+ require "pathname"
5
+
6
+ circle_config = {
7
+ "version" => "2.1",
8
+ "jobs" => {},
9
+ "workflows" => {
10
+ "version" => 2,
11
+ "all_rubies" => {
12
+ "jobs" => [
13
+ ],
14
+ },
15
+ }
16
+ }
17
+
18
+ supported_rubies = [
19
+ "2.6",
20
+ "2.7",
21
+ "3.0",
22
+ "3.1",
23
+ ]
24
+
25
+ supported_rubies.each do |ruby_verison|
26
+
27
+ test_results_dir = "/tmp/test-results/#{ruby_verison}"
28
+ job_name = "ruby__#{ruby_verison.gsub(/\./,"_")}"
29
+
30
+ job = {
31
+ "docker" => [
32
+ {
33
+ "image" => "cimg/ruby:#{ruby_verison}",
34
+ }
35
+ ],
36
+ "steps" => [
37
+ "checkout",
38
+ {
39
+ "run" => {
40
+ "name" => "Setup for build",
41
+ "command" => "bin/setup",
42
+ }
43
+ },
44
+ {
45
+ "run" => {
46
+ "name" => "Ensure bin/setup is idempotent",
47
+ "command" => "bin/setup",
48
+ }
49
+ },
50
+ {
51
+ "run" => {
52
+ "name" => "Create the test results dir",
53
+ "command" => "mkdir -p #{test_results_dir}",
54
+ }
55
+ },
56
+ {
57
+ "run" => {
58
+ "name" => "Run all tests",
59
+ "command" => "bin/ci #{test_results_dir}/rspec_results.xml",
60
+ }
61
+ },
62
+ {
63
+ "store_test_results" => {
64
+ "path" => test_results_dir,
65
+ }
66
+ },
67
+ {
68
+ "store_artifacts" => {
69
+ "path" => test_results_dir,
70
+ }
71
+ },
72
+ ]
73
+ }
74
+ circle_config["jobs"][job_name] = job
75
+ circle_config["workflows"]["all_rubies"]["jobs"] << job_name
76
+ end
77
+
78
+ circle_config_file = (Pathname(__FILE__).dirname / ".." / ".circleci" / "config.yml").expand_path
79
+ File.open(circle_config_file,"w") do |file|
80
+ file.puts "# THIS IS GENERATED - DO NOT EDIT"
81
+ file.puts "# regenerate with bin/mk_circle_config"
82
+ file.puts "# You are very welcome"
83
+ file.puts circle_config.to_yaml
84
+ end
@@ -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,14 +1,16 @@
1
1
  module WithClues
2
2
  class Html
3
3
  def dump(notifier, page:, context:)
4
- if !page.respond_to?(:html)
5
- notifier.notify "Something may be wrong. page (#{page.class}) does not respond to #html"
6
- return
7
- end
8
4
  notifier.blank_line
9
5
  notifier.notify "HTML {"
10
6
  notifier.blank_line
11
- notifier.notify_raw page.html
7
+ if page.respond_to?(:html)
8
+ notifier.notify_raw page.html
9
+ elsif page.respond_to?(:native)
10
+ notifier.notify_raw page.native
11
+ else
12
+ notifier.notify "[!] Something may be wrong. page (#{page.class}) does not respond to #html or #native"
13
+ end
12
14
  notifier.blank_line
13
15
  notifier.notify "} END HTML"
14
16
  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.0.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.0.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: 2021-03-20 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
@@ -76,6 +76,8 @@ files:
76
76
  - ".circleci/config.yml"
77
77
  - ".gitignore"
78
78
  - ".rspec"
79
+ - ".tool-versions"
80
+ - CHANGELOG.md
79
81
  - CODE_OF_CONDUCT.md
80
82
  - CONTRIBUTING.md
81
83
  - Gemfile
@@ -84,6 +86,7 @@ files:
84
86
  - Rakefile
85
87
  - bin/ci
86
88
  - bin/console
89
+ - bin/mk_circle_config
87
90
  - bin/mk_gem
88
91
  - bin/rake
89
92
  - bin/rspec
@@ -93,6 +96,7 @@ files:
93
96
  - lib/with_clues/html.rb
94
97
  - lib/with_clues/method.rb
95
98
  - lib/with_clues/notifier.rb
99
+ - lib/with_clues/private/custom_clue_method_analysis.rb
96
100
  - lib/with_clues/version.rb
97
101
  - with_clues.gemspec
98
102
  homepage: https://sustainable-rails.com
@@ -117,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
121
  - !ruby/object:Gem::Version
118
122
  version: '0'
119
123
  requirements: []
120
- rubygems_version: 3.1.2
124
+ rubygems_version: 3.3.3
121
125
  signing_key:
122
126
  specification_version: 4
123
127
  summary: WTF does this do?