topoisomerase 0.1.0 → 0.1.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 +4 -4
- data/.gitignore +1 -0
- data/ChangeLog +4 -0
- data/README.md +23 -1
- data/lib/topoisomerase.rb +66 -21
- data/lib/topoisomerase/comment_adder.rb +40 -0
- data/lib/topoisomerase/stub_template.rb.erb +5 -6
- data/lib/topoisomerase/template/page_object.rb +69 -0
- data/lib/topoisomerase/version.rb +2 -1
- data/todo.md +4 -0
- metadata +6 -4
- data/Gemfile.lock +0 -93
- data/lib/topoisomerase/override_define.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf5043b7dcc2f4ba70d7a034d42683de8975d67a0d2a92d51cab289b4f0b1996
|
4
|
+
data.tar.gz: 8d3ceca3787dbb577e9fb3e994a4180e5d5706c7cbc7ffd37c8238a7249131ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8b8ad151f546440ee8d9a64122680d0a4177fec31536d944f1c4cbefb76d4f7e79f8c2d7fe2735768dccb6c3d9867893b5d9fc3e37d704e56e2253a9eb11bff
|
7
|
+
data.tar.gz: f3ed5723e43bb23279e084f5efec789c6352fb610bad3eb6b6024ea730e0798ceba8df0f1738e8c6bdb8ebff059a2be836a501bd5a152770c5900b8fe291cd3e
|
data/.gitignore
CHANGED
data/ChangeLog
ADDED
data/README.md
CHANGED
@@ -34,7 +34,29 @@ Or install it yourself as:
|
|
34
34
|
|
35
35
|
## Usage
|
36
36
|
|
37
|
-
|
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
|
-
#
|
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 =
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
59
|
-
|
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
|
-
#
|
6
|
-
|
7
|
-
|
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
|
data/todo.md
ADDED
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.
|
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-
|
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
|