object_shadow 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.md +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/MIT-LICENSE.txt +20 -0
- data/README.md +195 -0
- data/Rakefile +38 -0
- data/lib/object_shadow.rb +17 -0
- data/lib/object_shadow/basic_object.rb +23 -0
- data/lib/object_shadow/info_inspect.rb +69 -0
- data/lib/object_shadow/instance_variables.rb +37 -0
- data/lib/object_shadow/method_introspection.rb +258 -0
- data/lib/object_shadow/object_method.rb +11 -0
- data/lib/object_shadow/version.rb +7 -0
- data/lib/object_shadow/wrap.rb +19 -0
- data/object_shadow.gemspec +21 -0
- data/spec/object_shadow_instance_variables_spec.rb +71 -0
- data/spec/object_shadow_method_introspection_spec.rb +914 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5ebcf54f524f3ab83fac7d05282f3510682a8e75b1c6f4744616aef84f2291f0
|
4
|
+
data.tar.gz: 6db42ef5f1cfff3b3927662cfddc774b779196521c023d3b224bf3b37a41e258
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7350dd92d83503c51dc1d4a3172d8eda8713f2dcee47d41fdc324c289ab809ff93410cb49a18f5297218536f5bb0dfbb38ca8a1b42d603049ce724a9afdac7dc
|
7
|
+
data.tar.gz: 516a2e61c66368c4cfef098878e1493eccb536e5a26788b861013875be14bccdaedf415674d08c8adef07f5906d5810c378da830556f723c45bfeeada5659a16
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at opensource@janlelis.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/MIT-LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2019 Jan Lelis, mail@janlelis.de
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
# Object#shadow [![[version]](https://badge.fury.io/rb/object.svg)](https://badge.fury.io/rb/object) [![[travis]](https://travis-ci.org/janlelis/object.svg)](https://travis-ci.org/janlelis/object)
|
2
|
+
|
3
|
+
Have you ever been [confused by some of Ruby's meta-programming methods](https://idiosyncratic-ruby.com/25-meta-methodology.html). If your answer is *Yes*, you have come to the right place:
|
4
|
+
|
5
|
+
![Object and Shadow](/object_shadow.png)
|
6
|
+
|
7
|
+
With **shadow**, every Ruby object has a shadow which encapsulates a clean API to access the object's variables and methods.
|
8
|
+
|
9
|
+
Never again you will have to do the `x.methods - Object.methods` trick to get a meaningful method list.
|
10
|
+
|
11
|
+
## Setup
|
12
|
+
|
13
|
+
Add to your `Gemfile`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem "object_shadow"
|
17
|
+
```
|
18
|
+
|
19
|
+
## Usage Example
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class P
|
23
|
+
def a_public_parent_method
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class C < P
|
28
|
+
def initialize
|
29
|
+
@ivar = 42
|
30
|
+
@another_variable = 43
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :another_variable
|
34
|
+
|
35
|
+
def a_public_method
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def a_protected_method
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def a_private_method
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
object = C.new
|
50
|
+
|
51
|
+
def object.a_public_singleton_method
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
### # Get an Overview
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
object.shadow
|
59
|
+
# ObjectShadow of Object #47023274596520
|
60
|
+
|
61
|
+
## Lookup Chain
|
62
|
+
|
63
|
+
#<Class:#<C:0x00005588eb283150>> → C → P → Object → …
|
64
|
+
|
65
|
+
## 2 Instance Variables
|
66
|
+
|
67
|
+
[:ivar, :another_variable]
|
68
|
+
|
69
|
+
## 4 Public Methods (Non-Class/Object)
|
70
|
+
|
71
|
+
[:a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable]
|
72
|
+
|
73
|
+
## 1 Protected Method (Non-Class/Object)
|
74
|
+
|
75
|
+
[:a_protected_method]
|
76
|
+
|
77
|
+
## 2 Private Methods (Non-Class/Object)
|
78
|
+
|
79
|
+
[:a_private_method, :initialize]
|
80
|
+
|
81
|
+
```
|
82
|
+
|
83
|
+
### # Read & Manipulate Instance Variables
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
object.shadow[:ivar] # => 42
|
87
|
+
object.shadow[:another_variable] = 23; object.another_variable # => 23
|
88
|
+
object.shadow.variables # => [:ivar, :another_variable]
|
89
|
+
object.shadow.to_h # => {:ivar=>42, :another_variable=>23}
|
90
|
+
object.shadow.remove(:ivar) # => 42 (and removed)
|
91
|
+
```
|
92
|
+
|
93
|
+
### # List Available Methods
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# shadow features a single method called `methods` which takes some keyword arguments for further listing options
|
97
|
+
object.shadow.methods # => [:a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable]
|
98
|
+
|
99
|
+
# Use scope: option to toggle visibility (default is public)
|
100
|
+
object.shadow.methods(scope: :public) # => [:a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable]
|
101
|
+
object.shadow.methods(scope: :protected) # => [:a_protected_method]
|
102
|
+
object.shadow.methods(scope: :private) # => [:a_private_method, :initialize]
|
103
|
+
object.shadow.methods(scope: :all) # => [:a_private_method, :a_protected_method, :a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable, :initialize]
|
104
|
+
|
105
|
+
# Use inherit: option to allow or prevent traversal of the inheritance chain
|
106
|
+
object.shadow.methods(scope: :public, inherit: :singleton) # => [:a_public_singleton_method]
|
107
|
+
object.shadow.methods(scope: :public, inherit: :self) # => [:a_public_method, :a_public_singleton_method, :another_variable]
|
108
|
+
object.shadow.methods(scope: :public, inherit: :exclude_object) # => [:a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable]
|
109
|
+
object.shadow.methods(scope: :public, inherit: :all) # => [:!, :!=, :!~, :<=>, :==, :===, :=~, :__id__, :__send__, :a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable, :class, :clone, :define_singleton_method, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :methods, :nil?, :object_id, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :remove_instance_variable, :respond_to?, :send, :shadow, :singleton_class, :singleton_method, :singleton_methods, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :untaint, :untrust, :untrusted?, :yield_self]
|
110
|
+
|
111
|
+
# Use target: :instances or :class to jump between child and class method listings
|
112
|
+
C.shadow.methods == C.new.shadow.methods(target: :class) #=> true
|
113
|
+
C.shadow.methods(target: :instances) == C.new.shadow.methods #=> true
|
114
|
+
Enumerable.shadow.methods(target: :instances) # (lists Enumerables' methods)
|
115
|
+
```
|
116
|
+
|
117
|
+
## Documentation
|
118
|
+
|
119
|
+
### Instance Variables
|
120
|
+
|
121
|
+
Shadow exposes instance variables in a Hash-like manner:
|
122
|
+
|
123
|
+
Method | Description
|
124
|
+
-----------|------------
|
125
|
+
`[]` | Retrieve instance variables. Takes a symbol without `@` to identify variable.
|
126
|
+
`[]=` | Sets instance variables. Takes a symbol without `@` to identify variable.
|
127
|
+
`remove` | Removes an instance variables. Takes a symbol without `@` to identify variable.
|
128
|
+
`variable?`| Checks if a variable with that name exists. Takes a symbol without `@` to identify variable.
|
129
|
+
`variables`| Returns the list of instance variables as symbols without `@`.
|
130
|
+
`to_h` | Returns a hash of instance variable names with `@`-less variables names as the keys.
|
131
|
+
`to_a` | Returns an array of all instance variable values.
|
132
|
+
|
133
|
+
### Method Introspection
|
134
|
+
|
135
|
+
All method introspection methods get called on the shadow and take a `target:` keyword argument, which defaults to `:self`. It can take one of the following values:
|
136
|
+
|
137
|
+
Value of `target:` | Meaning
|
138
|
+
-------------------|--------
|
139
|
+
`:self` | Operate on the current object
|
140
|
+
`:class` | Operate on the current object's class (the class for instances, the singleton class for classes)
|
141
|
+
`:instances` | Operate on potential instances created by the object, which is a class (or module)
|
142
|
+
|
143
|
+
#### `methods(target: :self, scope: :public, inherit: :exclude_class)`
|
144
|
+
|
145
|
+
Returns a list of methods available to the object.
|
146
|
+
|
147
|
+
Only shows methods matching the given `scope:`, i.e. when you request all **public** methods, **protected** and **private** methods will not be included. You can also pass in `:all` to get methods of *all* scopes.
|
148
|
+
|
149
|
+
The `inherit:` option lets you choose how deep you want to dive into the inheritance chain:
|
150
|
+
|
151
|
+
Value of `inherit:` | Meaning
|
152
|
+
--------------------|--------
|
153
|
+
`:singleton` | Show only methods directly defined in the object's singleton class
|
154
|
+
`:self` | Show singleton methods and methods directly defined in the object's class, but do not traverse the inheritance chain
|
155
|
+
`:exclude_class` | Stop inheritance chain just before Class or Module. For non-modules it fallbacks to `:exclude_object`
|
156
|
+
`:exclude_object` | Stop inheritance chain just before Object
|
157
|
+
`:all` | Show methods from the whole inheritance chain
|
158
|
+
|
159
|
+
#### `method?(method_name, target: :self)`
|
160
|
+
|
161
|
+
Returns `true` if such a method can be found, `false` otherwise
|
162
|
+
|
163
|
+
#### `method_scope(method_name, target: :self)`
|
164
|
+
|
165
|
+
Returns the visibility scope of the method in question, one of `:public`, `:protected`, `:private`. If the method cannot be located, returns `nil`.
|
166
|
+
|
167
|
+
#### `method(method_name, target: :self, unbind: false, all: false)`
|
168
|
+
|
169
|
+
Returns the `Method` or `UnboundMethod` object of the method requested. Use `unbind: true` to force the return value to be an `UnboundMethod` object. Will always return `UnboundMethod`s if used in conjunction with `target: :instances`.
|
170
|
+
|
171
|
+
If you pass in `all: true`, it will return an array of all (unbound) method objects found in the inheritance chain for the given method name.
|
172
|
+
|
173
|
+
#### `method_lookup_chain(target: :self, inherit: :exclude_class)`
|
174
|
+
|
175
|
+
Shows the lookup chain for the target. See `methods()` for description of the `inherit:` option.
|
176
|
+
|
177
|
+
## Q & A
|
178
|
+
|
179
|
+
### Can I Access Hidden Instance Variables?
|
180
|
+
|
181
|
+
Some of Ruby's core classes use `@`-less instance variables, such as [Structs](https://ruby-doc.org/core/Struct.html). They cannot be accessed using shadow.
|
182
|
+
|
183
|
+
### Does It Support Refinements?
|
184
|
+
|
185
|
+
[Currently not.](https://ruby-doc.org/core/doc/syntax/refinements_rdoc.html#label-Methods+Introspection)
|
186
|
+
|
187
|
+
### Other Meta Programming?
|
188
|
+
|
189
|
+
Only some aspects of Ruby meta-programming are covered. However, **shadow** aims to cover all kinds of meta-programming. Maybe you have an idea about how to integrate `eval`, `method_missing`, and friends?
|
190
|
+
|
191
|
+
## J-_-L
|
192
|
+
|
193
|
+
Copyright (C) 2019 Jan Lelis <https://janlelis.com>. Released under the MIT license.
|
194
|
+
|
195
|
+
PS: This gem would not exist if the [instance gem](https://rubyworks.github.io/instance/) did not come up with the idea.
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# # #
|
2
|
+
# Get gemspec info
|
3
|
+
|
4
|
+
gemspec_file = Dir["*.gemspec"].first
|
5
|
+
gemspec = eval File.read(gemspec_file), binding, gemspec_file
|
6
|
+
info = "#{gemspec.name} | #{gemspec.version} | " \
|
7
|
+
"#{gemspec.runtime_dependencies.size} dependencies | " \
|
8
|
+
"#{gemspec.files.size} files"
|
9
|
+
|
10
|
+
# # #
|
11
|
+
# Gem build and install task
|
12
|
+
|
13
|
+
desc info
|
14
|
+
task :gem do
|
15
|
+
puts info + "\n\n"
|
16
|
+
print " "; sh "gem build #{gemspec_file}"
|
17
|
+
FileUtils.mkdir_p "pkg"
|
18
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", "pkg"
|
19
|
+
puts; sh %{gem install --no-document pkg/#{gemspec.name}-#{gemspec.version}.gem}
|
20
|
+
end
|
21
|
+
|
22
|
+
# # #
|
23
|
+
# Start an IRB session with the gem loaded
|
24
|
+
|
25
|
+
desc "#{gemspec.name} | IRB"
|
26
|
+
task :irb do
|
27
|
+
sh "irb -I ./lib -r #{gemspec.name.gsub '-','/'}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# # #
|
31
|
+
# Run specs
|
32
|
+
|
33
|
+
desc "#{gemspec.name} | Spec"
|
34
|
+
task :spec do
|
35
|
+
sh "for file in spec/*_spec.rb; do ruby $file; done"
|
36
|
+
end
|
37
|
+
task default: :spec
|
38
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "object_shadow/basic_object"
|
4
|
+
require_relative "object_shadow/version"
|
5
|
+
require_relative "object_shadow/object_method"
|
6
|
+
|
7
|
+
require_relative "object_shadow/wrap"
|
8
|
+
require_relative "object_shadow/instance_variables"
|
9
|
+
require_relative "object_shadow/method_introspection"
|
10
|
+
require_relative "object_shadow/info_inspect"
|
11
|
+
|
12
|
+
class ObjectShadow
|
13
|
+
include Wrap
|
14
|
+
include InstanceVariables
|
15
|
+
include MethodIntrospection
|
16
|
+
include InfoInspect
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ObjectShadow < BasicObject
|
4
|
+
def class
|
5
|
+
::ObjectShadow
|
6
|
+
end
|
7
|
+
|
8
|
+
def respond_to?(what)
|
9
|
+
::ObjectShadow.instance_methods.include?(what)
|
10
|
+
end
|
11
|
+
|
12
|
+
def instance_of?(other)
|
13
|
+
other == ::ObjectShadow
|
14
|
+
end
|
15
|
+
|
16
|
+
def is_a?(other)
|
17
|
+
other.ancestors.include? ::ObjectShadow
|
18
|
+
end
|
19
|
+
|
20
|
+
def singleton_class
|
21
|
+
class << self; self end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ObjectShadow
|
4
|
+
module InfoInspect
|
5
|
+
def inspect
|
6
|
+
public_methods = methods(scope: :public)
|
7
|
+
protected_methods = methods(scope: :protected)
|
8
|
+
private_methods = methods(scope: :private)
|
9
|
+
|
10
|
+
inherit_till = object.instance_of?(Object) ? :all : Object
|
11
|
+
lookup_chain = method_lookup_chain(inherit: inherit_till).join(" → ") + " → …"
|
12
|
+
|
13
|
+
res = \
|
14
|
+
"# ObjectShadow of Object #%{object_id}\n\n" \
|
15
|
+
"## Lookup Chain\n\n%{method_lookup_chain}\n\n"
|
16
|
+
|
17
|
+
unless variables.empty?
|
18
|
+
res += "## %{variables_count} Instance Variable%{variables_plural}\n\n%{variables}\n\n" \
|
19
|
+
end
|
20
|
+
|
21
|
+
unless public_methods.empty?
|
22
|
+
res += "## %{public_methods_count} Public Method%{public_methods_plural} (Non-Class/Object)\n\n%{public_methods}\n\n" \
|
23
|
+
end
|
24
|
+
|
25
|
+
unless protected_methods.empty?
|
26
|
+
res += "## %{protected_methods_count} Protected Method%{protected_methods_plural} (Non-Class/Object)\n\n%{protected_methods}\n\n" \
|
27
|
+
end
|
28
|
+
|
29
|
+
unless private_methods.empty?
|
30
|
+
res += "## %{private_methods_count} Private Method%{private_methods_plural} (Non-Class/Object)\n\n%{private_methods}\n\n" \
|
31
|
+
end
|
32
|
+
|
33
|
+
res % {
|
34
|
+
object_id: object.object_id,
|
35
|
+
method_lookup_chain: InfoInspect.column100(lookup_chain),
|
36
|
+
variables_count: variables.size,
|
37
|
+
variables_plural: variables.size == 1 ? "" : "s",
|
38
|
+
variables: InfoInspect.column100(variables.inspect),
|
39
|
+
public_methods_count: public_methods.size,
|
40
|
+
public_methods_plural: public_methods.size == 1 ? "" : "s",
|
41
|
+
public_methods: InfoInspect.column100(public_methods.inspect),
|
42
|
+
protected_methods_count: protected_methods.size,
|
43
|
+
protected_methods_plural: protected_methods.size == 1 ? "" : "s",
|
44
|
+
protected_methods: InfoInspect.column100(protected_methods.inspect),
|
45
|
+
private_methods_count: private_methods.size,
|
46
|
+
private_methods_plural: private_methods.size == 1 ? "" : "s",
|
47
|
+
private_methods: InfoInspect.column100(private_methods.inspect),
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def column100(input)
|
53
|
+
words = input.split(" ")
|
54
|
+
lines = [""]
|
55
|
+
words.each{ |word|
|
56
|
+
if lines[-1].size + word.size < 95 # 95 + 1 word space + 4 indent spaces = 95
|
57
|
+
lines[-1] = lines[-1] + word + " "
|
58
|
+
else
|
59
|
+
lines << word + " "
|
60
|
+
end
|
61
|
+
}
|
62
|
+
|
63
|
+
lines.map{ |line|
|
64
|
+
" #{line}"
|
65
|
+
}.join("\n")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|