topoisomerase 0.1.0 → 0.1.1

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: b85e0899515e89773ce6a546b58fab50d4fb802d8752ca7f7836c254ea072dd5
4
- data.tar.gz: 0773b1feacf0423d6e14b8793e8dee8074bfb0e4e355611a2902a9e58d7b24b8
3
+ metadata.gz: cf5043b7dcc2f4ba70d7a034d42683de8975d67a0d2a92d51cab289b4f0b1996
4
+ data.tar.gz: 8d3ceca3787dbb577e9fb3e994a4180e5d5706c7cbc7ffd37c8238a7249131ab
5
5
  SHA512:
6
- metadata.gz: c3323e825f54fcb67243e7d763743ecd2337635a7c0e3a2d132e1264b7bba2c5f2c5ac21c9da1198bd8b49d3c913f6fdeb69d7697736666b0f43610e86258da8
7
- data.tar.gz: d00d0ff0f245067be77d161e66e2255364993777aa878eb107057b6ea98ca4146bb427450687f4278d45352f7e75ef974e5d30076245900e85e304ef03deacd3
6
+ metadata.gz: b8b8ad151f546440ee8d9a64122680d0a4177fec31536d944f1c4cbefb76d4f7e79f8c2d7fe2735768dccb6c3d9867893b5d9fc3e37d704e56e2253a9eb11bff
7
+ data.tar.gz: f3ed5723e43bb23279e084f5efec789c6352fb610bad3eb6b6024ea730e0798ceba8df0f1738e8c6bdb8ebff059a2be836a501bd5a152770c5900b8fe291cd3e
data/.gitignore CHANGED
@@ -8,6 +8,7 @@
8
8
  /tmp/
9
9
  /.idea/
10
10
  /stub/
11
+ Gemfile.lock
11
12
 
12
13
  # rspec failure tracking
13
14
  .rspec_status
data/ChangeLog ADDED
@@ -0,0 +1,4 @@
1
+ Version 0.1.1
2
+ * Use 'comments_added' to add comments matching a regular expression
3
+ Version 0.1.0
4
+ * Initial working of the gem making basic stubs for dynamic methods objects inheriting from a class
data/README.md CHANGED
@@ -34,7 +34,29 @@ Or install it yourself as:
34
34
 
35
35
  ## Usage
36
36
 
37
- TODO: Write usage instructions here
37
+ To create a stub file for a class, call the `create_stubs_for` passing that class.
38
+
39
+ E.g:
40
+ ```ruby
41
+ Topoisomerase.create_stubs_for BasicClass
42
+ ```
43
+
44
+ By default stubs will be created in the `stubs` folder.
45
+ This can be changed by setting the `stub_folder` variable.
46
+
47
+ ```ruby
48
+ Topoisomerase.stub_folder = 'custom_folder'
49
+ ```
50
+
51
+ Note that this will by default attempt to instantiate the class with ClassName.new. If parameters are
52
+ needed pass a block to the `create_stubs_for` as shown below. The result of the block will be the
53
+ instance of the class used in creating stubs.
54
+
55
+ ```ruby
56
+ Topoisomerase.create_stubs_for ParameterizedClass do
57
+ ParameterizedClass.new 5
58
+ end
59
+ ```
38
60
 
39
61
  ## Development
40
62
 
data/lib/topoisomerase.rb CHANGED
@@ -2,49 +2,92 @@ require 'topoisomerase/version'
2
2
  require 'method_source'
3
3
  require 'fileutils'
4
4
  require 'topoisomerase/core_ext/string'
5
+ require 'topoisomerase/comment_adder'
5
6
 
6
- # USE PRY instead
7
+ # Module for parsing and creating stubs for dynamic methods
7
8
  module Topoisomerase
8
9
  @stub_folder = 'stub'
10
+ @comments_added = {}
11
+ # @return [Object] Class to create stubs for
12
+ @class_name = nil
9
13
 
14
+ # For errors raised related to Topoisomerase
10
15
  class Error < StandardError; end
11
- # Your code goes here...
12
16
  class << self
13
17
  # @return [String] Folder where stubs are stored
14
18
  attr_accessor :stub_folder
19
+ # @todo Use custom object for this
20
+ # @return [Hash] Comments added to stubs generated grouped by inheriting class and then by
21
+ # message, matcher patterns
22
+ attr_accessor :comments_added
15
23
 
24
+ # @todo This is only covering 'define_method'. More methods will be added later
25
+ # @return [Boolean] Whether method is defined dynamically
16
26
  def dynamic_method?(method)
17
27
  source_code = method.source
18
28
  source_code.strip.start_with? 'define_method'
19
29
  end
20
30
 
21
31
  # @param [Object] class_name Class to retrieve dynamic methods for
22
- # @return [Hash]
32
+ # @return [Hash] Hash with dynamic methods as keys and values being the source, comment
33
+ # and location
23
34
  def dynamic_instance_methods(class_name)
24
- class_instance = if block_given?
25
- yield
26
- else
27
- class_name.new
28
- end
35
+ class_instance = block_given? ? yield(class_name) : class_name.new
29
36
  methods_hash = {}
30
37
  (class_instance.public_methods - Object.public_methods).each do |method|
31
38
  method_obj = class_instance.method(method)
32
- if dynamic_method? class_instance.method(method)
33
- methods_hash.merge!(method => { source: method_obj.source, comment: method_obj.comment,
34
- location: method_obj.source_location })
35
- end
39
+ next unless dynamic_method? class_instance.method(method)
40
+
41
+ methods_hash.merge!(method => { source: method_obj.source, comment: method_obj.comment,
42
+ location: method_obj.source_location,
43
+ parameters: method_obj.parameters.collect { |p| p.last.to_s } })
44
+ # TODO: May be worth checking required parameters and documenting non-required differently
45
+ # receiver - Shows object that will call method
46
+ # owner - shows class from which method belongs to
47
+ # parameters - Array with params and :req
36
48
  end
37
- # methods.each do |method|
38
- # method_obj = class_instance.method(method)
39
- # methods_hash.merge(method => { source: method_obj.source, comment: method_obj.comment,
40
- # location: method_obj.source_location })
41
- # end
42
49
  methods_hash
43
50
  end
44
51
 
52
+ # @return [String] Comment extracted from dynamic element
53
+ def extracted_comment(method_data)
54
+ method_data[:comment].strip.empty? ? '' : "Extracted comment #{method_data[:comment].strip}"
55
+ end
56
+
57
+ # @return [Boolean]
58
+ def match?(value_to_test, matcher_value)
59
+ value_to_test = value_to_test.to_s
60
+ case matcher_value
61
+ when Regexp then !value_to_test[matcher_value].nil?
62
+ when String then value_to_test == matcher_value
63
+ else
64
+ raise "Unknown matcher type for #{value_to_test}, value of #{matcher_value}"
65
+ end
66
+ end
67
+
68
+ # Reads from 'comments_added' variable if no comment added above dynamic method
69
+ # @return [String] Comment defined by 'comments_added'
70
+ def comment_for(method_to_stub, method_data)
71
+ return extracted_comment(method_data) unless comments_added[@inheriting_class]
72
+
73
+ comments_added[@inheriting_class].each do |comment_matcher|
74
+ matchers = comment_matcher[:matchers]
75
+ return ERB.new(comment_matcher[:message]).result(binding) if matchers.all? do |matcher_type, matcher_value|
76
+ extracted_match = case matcher_type
77
+ when :method_name then match?(method_to_stub, matcher_value)
78
+ when :source then match? method_data[:source], matcher_value
79
+ else
80
+ raise Topoisomerase::Error, "Undefined matcher #{matcher_type}"
81
+ end
82
+ extracted_match
83
+ end
84
+ end
85
+ ''
86
+ end
87
+
45
88
  # Create stub file for passed in class
46
89
  # @param [Object] class_name Object to create stub file for
47
- def create_stubs_for(class_name)
90
+ def create_stubs_for(class_name, inner_folder = nil)
48
91
  template = File.join(File.dirname(__FILE__), 'topoisomerase', 'stub_template.rb.erb')
49
92
  @class_name = class_name
50
93
  @class_instance = if block_given?
@@ -55,8 +98,9 @@ module Topoisomerase
55
98
  @dynamic_methods = dynamic_instance_methods(class_name) do
56
99
  @class_instance
57
100
  end
58
- FileUtils.mkdir_p stub_folder
59
- filename = File.join(stub_folder, "#{class_name.to_s.snakecase}.rb")
101
+ class_folder = inner_folder ? File.join(stub_folder, inner_folder) : stub_folder
102
+ FileUtils.mkdir_p class_folder
103
+ filename = File.join(class_folder, "#{class_name.to_s.snakecase}.rb")
60
104
  IO.write filename, ERB.new(File.read(template)).result(binding)
61
105
  `rubocop -a #{filename}`
62
106
  end
@@ -64,12 +108,13 @@ module Topoisomerase
64
108
  # Create stub files for each class that inherits from passed in class
65
109
  # @param [Object] inheriting_class Object to check for inheriting classes for
66
110
  def create_stubs_based_on(inheriting_class)
111
+ @inheriting_class = inheriting_class
67
112
  classes = ObjectSpace.each_object(Class).select { |class_name| class_name < inheriting_class }.reject do |class_name|
68
113
  class_name.to_s.split(':')[0] == inheriting_class.to_s
69
114
  end
70
115
  classes.each do |class_name|
71
116
  @class_instance = block_given? ? yield(class_name) : class_name.new
72
- create_stubs_for(class_name) { @class_instance }
117
+ create_stubs_for(class_name, inheriting_class.to_s.snakecase) { @class_instance }
73
118
  end
74
119
  end
75
120
  end
@@ -0,0 +1,40 @@
1
+ module Topoisomerase
2
+ # Class to set up comments that will be added to stubs for dynamic methods
3
+ class Comments
4
+ class << self
5
+ # @return [Object] Class for which stubs will be created from and for
6
+ # which rules to add comments for are defined
7
+ attr_accessor :inheriting_class
8
+
9
+ # Add a comment that is returned if ALL matchers are true
10
+ # @example Match a method ending with '_element' and include it's name without the '_element'
11
+ # comment "@return [Object] The '<%= method_to_stub.to_s.gsub('_element', '') %>' element",
12
+ # method_name: /_element$/
13
+ # @param [String] comment Comment to add above class
14
+ def comment(comment, matchers)
15
+ Topoisomerase.comments_added[inheriting_class] << {
16
+ message: comment,
17
+ matchers: matchers
18
+ }
19
+ end
20
+
21
+ # Add comments according to matchers for an inheriting class
22
+ #
23
+ # The order in which they are added is the order in which matching will occur.
24
+ # The first comment that has a match will be used.
25
+ # If several matchers are used on 1 comment, all must be true to be considered a match
26
+ #
27
+ # @example Add a comment where method name ends with '=' and source contains '.text'
28
+ # Comments.add_for_class PageObject do
29
+ # comment 'Comment to add', method_name: /=$/, source: /.text/
30
+ # end
31
+ #
32
+ # @param [Object] inheriting_class Class for which stubs will be created from and for
33
+ # which rules to add comments for are defined
34
+ def add_for_class(inheriting_class, &script)
35
+ self.inheriting_class = inheriting_class
36
+ instance_eval(&script)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -2,13 +2,12 @@ require 'topoisomerase'
2
2
  # Stub for <%= @class_name %> created by 'topoisomerase'
3
3
  class <%= @class_name %>
4
4
  <% @dynamic_methods.each do |method_to_stub, method_data| %>
5
- # Defined at <%= method_data[:location] %>
6
- # Extracted comment <%= method_data[:comment].strip %>
7
- def <%= method_to_stub %>
8
- raise Topoisomerase::Error, "Method '<%= method_to_stub %>' called when it
5
+ # <%= comment_for(method_to_stub, method_data) %>
6
+ def <%= method_to_stub %> <%= method_data[:parameters].join(',') %>
7
+ # Defined at <%= method_data[:location] %>
8
+ raise Topoisomerase::Error, "Method '<%= method_to_stub %>' called with when it
9
9
  should not have been. Don't require stub files"
10
- # This is merely a stub
11
- # Contents is below
10
+ # This is merely a stub. Contents is below
12
11
  <%= method_data[:source] %>
13
12
  end
14
13
  <% end %>
@@ -0,0 +1,69 @@
1
+ # Load comments for page_object and create stubs for classes inheriting from it
2
+
3
+ # This blocks shows the manual addition of comments to the comments_added accessor
4
+ # Preferred way is to Topoisomerase::Comments.add_for_class
5
+ #
6
+ # Topoisomerase.comments_added[PageObject] = [
7
+ # {
8
+ # message: "@return [PageObject::Elements::Element] An object representing the '<%= method_to_stub.to_s.gsub('_element', '') %>' element",
9
+ # matchers: { method_name: /_element$/ } # If either matcher matches, result returned
10
+ # },
11
+ # {
12
+ # message: "@return [Boolean] Whether the '<%= method_to_stub.to_s.gsub('?', '') %>' element is present",
13
+ # matchers: { method_name: /\?$/ }
14
+ # },
15
+ # # Testing whether 2 matchers use AND
16
+ # {
17
+ # message: "Set the text of the '<%= method_to_stub.to_s.gsub('?', '') %>' text field\n # @param [String] _value Value to set",
18
+ # matchers: { method_name: /=$/, source: /text_field_value_set/ }
19
+ # },
20
+ # {
21
+ # message: "Set the value of '<%= method_to_stub.to_s.gsub('?', '') %>' element\n # @param [String] _value Value to set",
22
+ # matchers: { method_name: /=$/ }
23
+ # },
24
+ # {
25
+ # message: "@return [String] The text of the '<%= method_to_stub.to_s.gsub('?', '') %>' element",
26
+ # matchers: { source: /\.text/ }
27
+ # },
28
+ # {
29
+ # message: "Click the '<%= method_to_stub.to_s.gsub('?', '') %>' element",
30
+ # matchers: { source: /\.click/ }
31
+ # }
32
+ # ]
33
+
34
+
35
+ # message: "@return [Watir::Elements::Element] An object representing the '<%= method_to_stub.to_s.gsub('_element', '') %>' element",
36
+ # matchers: { method_name: /_element$/ } # If either matcher matches, result returned
37
+ # },
38
+
39
+ Topoisomerase::Comments.add_for_class PageObject do
40
+ comment "Url of the page. Used by 'goto' method and when using 'visit'", method_name: 'page_url_value'
41
+ comment 'Open the page on the browser', method_name: 'goto'
42
+ comment "@return [PageObject::Elements::Element] An object representing the '<%= method_to_stub.to_s.gsub('_element', '') %>' element",
43
+ method_name: /_element$/
44
+ end
45
+
46
+ # This block demonstrates how a real browser could be used to create stubs.
47
+ # This however should not be necessary and be resorted to only if the MockBrowser does not
48
+ # work
49
+ #
50
+ # @return [Array] List of Selenium options
51
+ # def options
52
+ # [args: %w[--disable-popup-blocking
53
+ # --no-sandbox --disable-dev-shm-usage],
54
+ # headless: true]
55
+ # end
56
+ #
57
+ # require 'watir'
58
+ # browser = Watir::Browser.new :chrome, *options
59
+ # browser.close
60
+
61
+ # Mock class just to make instantiating a page object with a browser happy
62
+ class MockBrowser
63
+ # Called by PageObject though not needed for documentation
64
+ def bridge; end
65
+ end
66
+
67
+ Topoisomerase.create_stubs_based_on PageObject do |inheriting_class|
68
+ inheriting_class.new(MockBrowser.new, false)
69
+ end
@@ -1,3 +1,4 @@
1
1
  module Topoisomerase
2
- VERSION = '0.1.0'.freeze
2
+ # @return [String] Version of the gem
3
+ VERSION = '0.1.1'.freeze
3
4
  end
data/todo.md ADDED
@@ -0,0 +1,4 @@
1
+ * Handle other ways of dynamically defining methods
2
+ * define_singleton_method
3
+ * instance_eval
4
+ * etc
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: topoisomerase
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Garratt
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-08 00:00:00.000000000 Z
11
+ date: 2019-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: method_source
@@ -135,8 +135,8 @@ files:
135
135
  - ".rspec"
136
136
  - ".rubocop.yml"
137
137
  - CODE_OF_CONDUCT.md
138
+ - ChangeLog
138
139
  - Gemfile
139
- - Gemfile.lock
140
140
  - LICENSE.txt
141
141
  - README.md
142
142
  - Rakefile
@@ -144,10 +144,12 @@ files:
144
144
  - bin/setup
145
145
  - exe/topoisomerase
146
146
  - lib/topoisomerase.rb
147
+ - lib/topoisomerase/comment_adder.rb
147
148
  - lib/topoisomerase/core_ext/string.rb
148
- - lib/topoisomerase/override_define.rb
149
149
  - lib/topoisomerase/stub_template.rb.erb
150
+ - lib/topoisomerase/template/page_object.rb
150
151
  - lib/topoisomerase/version.rb
152
+ - todo.md
151
153
  - topoisomerase.gemspec
152
154
  homepage: https://gitlab.com/samuel-garratt/topoisomerase
153
155
  licenses:
data/Gemfile.lock DELETED
@@ -1,93 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- topoisomerase (0.1.0)
5
- method_source
6
- rubocop
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- ast (2.4.0)
12
- childprocess (1.0.1)
13
- rake (< 13.0)
14
- concurrent-ruby (1.1.5)
15
- data_magic (1.2)
16
- faker (>= 1.1.2)
17
- yml_reader (>= 0.6)
18
- diff-lcs (1.3)
19
- faker (1.9.3)
20
- i18n (>= 0.7)
21
- i18n (1.6.0)
22
- concurrent-ruby (~> 1.0)
23
- jaro_winkler (1.5.2)
24
- method_source (0.9.2)
25
- mini_portile2 (2.4.0)
26
- nokogiri (1.10.3)
27
- mini_portile2 (~> 2.4.0)
28
- page-object (2.2.5)
29
- page_navigation (>= 0.10)
30
- selenium-webdriver (~> 3.0)
31
- watir (~> 6.8)
32
- page_navigation (0.10)
33
- data_magic (>= 0.22)
34
- parallel (1.17.0)
35
- parser (2.6.0.0)
36
- ast (~> 2.4.0)
37
- powerpack (0.1.2)
38
- psych (3.1.0)
39
- rainbow (3.0.0)
40
- rake (10.5.0)
41
- regexp_parser (1.3.0)
42
- rspec (3.8.0)
43
- rspec-core (~> 3.8.0)
44
- rspec-expectations (~> 3.8.0)
45
- rspec-mocks (~> 3.8.0)
46
- rspec-core (3.8.0)
47
- rspec-support (~> 3.8.0)
48
- rspec-expectations (3.8.3)
49
- diff-lcs (>= 1.2.0, < 2.0)
50
- rspec-support (~> 3.8.0)
51
- rspec-mocks (3.8.0)
52
- diff-lcs (>= 1.2.0, < 2.0)
53
- rspec-support (~> 3.8.0)
54
- rspec-support (3.8.0)
55
- rubocop (0.65.0)
56
- jaro_winkler (~> 1.5.1)
57
- parallel (~> 1.10)
58
- parser (>= 2.5, != 2.5.1.1)
59
- powerpack (~> 0.1)
60
- psych (>= 3.1.0)
61
- rainbow (>= 2.2.2, < 4.0)
62
- ruby-progressbar (~> 1.7)
63
- unicode-display_width (~> 1.4.0)
64
- ruby-progressbar (1.10.0)
65
- rubyzip (1.2.2)
66
- selenium-webdriver (3.142.0)
67
- childprocess (>= 0.5, < 2.0)
68
- rubyzip (~> 1.2, >= 1.2.2)
69
- unicode-display_width (1.4.1)
70
- watir (6.16.5)
71
- regexp_parser (~> 1.2)
72
- selenium-webdriver (~> 3.6)
73
- webdrivers (3.8.0)
74
- nokogiri (~> 1.6)
75
- rubyzip (~> 1.0)
76
- selenium-webdriver (~> 3.0)
77
- yard (0.9.19)
78
- yml_reader (0.7)
79
-
80
- PLATFORMS
81
- ruby
82
-
83
- DEPENDENCIES
84
- bundler (~> 2.0)
85
- page-object
86
- rake
87
- rspec (~> 3.0)
88
- topoisomerase!
89
- webdrivers
90
- yard
91
-
92
- BUNDLED WITH
93
- 2.0.1
@@ -1,13 +0,0 @@
1
- # This is very dangerous. Overriding define_method.
2
- # Only to be used for specific task of documenting dynamic methods used
3
- class Object
4
- def self.define_method(method_name)
5
- Topoisomerase.dynamic_methods[self] = method_name
6
- end
7
-
8
- # I don't think I should include this. Safer and makes sense
9
- # just to focus on definitions in custom classes
10
- # def define_method(method_name)
11
- # Topoisomerase.dynamic_methods[self] = method_name
12
- # end
13
- end