peeky 0.0.19 → 0.0.33
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/.rubocop.yml +21 -0
- data/.rubocop_todo.yml +4 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +6 -3
- data/Guardfile +4 -4
- data/README.md +96 -9
- data/{README-stories.md → STORIES.md} +28 -4
- data/USAGE.md +438 -0
- data/lib/peeky.rb +4 -0
- data/lib/peeky/api.rb +74 -0
- data/lib/peeky/class_info.rb +139 -29
- data/lib/peeky/example/yard_sample.rb +124 -0
- data/lib/peeky/method_info.rb +5 -4
- data/lib/peeky/parameter_info.rb +96 -21
- data/lib/peeky/predicates/attr_reader_predicate.rb +16 -4
- data/lib/peeky/predicates/attr_writer_predicate.rb +8 -0
- data/lib/peeky/renderer/class_debug_render.rb +95 -0
- data/lib/peeky/renderer/class_interface_render.rb +9 -9
- data/lib/peeky/renderer/class_interface_yard_render.rb +141 -0
- data/lib/peeky/renderer/method_call_minimum_params_render.rb +4 -5
- data/lib/peeky/renderer/method_signature_render.rb +44 -13
- data/lib/peeky/renderer/method_signature_with_debug_render.rb +11 -11
- data/lib/peeky/version.rb +1 -1
- data/peeky.gemspec +15 -3
- metadata +35 -8
data/lib/peeky.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'peeky/version'
|
4
4
|
|
5
|
+
require 'peeky/api'
|
6
|
+
|
5
7
|
require 'peeky/attr_info'
|
6
8
|
require 'peeky/class_info'
|
7
9
|
require 'peeky/method_info'
|
@@ -10,7 +12,9 @@ require 'peeky/parameter_info'
|
|
10
12
|
require 'peeky/predicates/attr_reader_predicate'
|
11
13
|
require 'peeky/predicates/attr_writer_predicate'
|
12
14
|
|
15
|
+
require 'peeky/renderer/class_debug_render'
|
13
16
|
require 'peeky/renderer/class_interface_render'
|
17
|
+
require 'peeky/renderer/class_interface_yard_render'
|
14
18
|
require 'peeky/renderer/method_call_minimum_params_render'
|
15
19
|
require 'peeky/renderer/method_signature_render'
|
16
20
|
require 'peeky/renderer/method_signature_with_debug_render'
|
data/lib/peeky/api.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Peeky module provides access to the API via the
|
4
|
+
# module method Peeky.api
|
5
|
+
module Peeky
|
6
|
+
class << self
|
7
|
+
attr_accessor :api
|
8
|
+
end
|
9
|
+
|
10
|
+
# API has factory and creational patterns for easy usage
|
11
|
+
# of the Peeky reflection system
|
12
|
+
class Api
|
13
|
+
# Build a {Peeky::ClassInfo} structure based around a
|
14
|
+
# ruby class instance.
|
15
|
+
#
|
16
|
+
# ClassInfo stores information about the instance of a
|
17
|
+
# class that is passed in including methods, attr_accessors
|
18
|
+
# attr_readers and attr_writers.
|
19
|
+
def build_class_info(instance, lazy: true)
|
20
|
+
ci = Peeky::ClassInfo.new(instance)
|
21
|
+
ci.load unless lazy
|
22
|
+
ci
|
23
|
+
end
|
24
|
+
|
25
|
+
# Render a class using a predefined class renderer
|
26
|
+
def render_class(render_key, class_info: nil, instance: nil, **_opts)
|
27
|
+
raise 'Call render_class with class_info OR instance.' if class_info.nil? && instance.nil?
|
28
|
+
raise 'Call render_class with class_info OR instance, these parameters are mutually exclusive' if !class_info.nil? && !instance.nil?
|
29
|
+
|
30
|
+
renderer = class_renderer(render_key)
|
31
|
+
|
32
|
+
class_info = Peeky::ClassInfo.new(instance) if class_info.nil?
|
33
|
+
|
34
|
+
renderer.new(class_info).render
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get a method renderer by :key
|
38
|
+
#
|
39
|
+
# TODO: Refactor to a configurable system
|
40
|
+
def method_renderer(key)
|
41
|
+
case key
|
42
|
+
when :signature
|
43
|
+
Peeky::Renderer::MethodSignatureRender
|
44
|
+
when :signature_with_debug
|
45
|
+
Peeky::Renderer::MethodSignatureWithDebugRender
|
46
|
+
when :call_minimum_params
|
47
|
+
Peeky::Renderer::MethodCallMinimumParamsRender
|
48
|
+
else
|
49
|
+
raise "Unknown method renderer: #{key}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get a class renderer by :key
|
54
|
+
#
|
55
|
+
# TODO: Refactor to a configurable system
|
56
|
+
def class_renderer(key)
|
57
|
+
case key
|
58
|
+
when :class_debug
|
59
|
+
Peeky::Renderer::ClassDebugRender
|
60
|
+
when :class_interface
|
61
|
+
Peeky::Renderer::ClassInterfaceRender
|
62
|
+
when :class_interface_yard
|
63
|
+
Peeky::Renderer::ClassInterfaceYardRender
|
64
|
+
else
|
65
|
+
raise "Unknown class renderer: #{key}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# def render_method()
|
70
|
+
# end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Peeky.api = Peeky::Api.new
|
data/lib/peeky/class_info.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Peeky
|
4
|
-
# Class Info stores information about the
|
5
|
-
# class instance that you pass in.
|
4
|
+
# Class Info stores information about the class instance that is provided.
|
6
5
|
#
|
7
6
|
# The information is collected into MethodInfo objects
|
8
7
|
# that live within the signatures accessor.
|
@@ -10,6 +9,7 @@ module Peeky
|
|
10
9
|
# This information is then separated out into
|
11
10
|
# :methods, :attr_accessors, :attr_readers and :attr_writers
|
12
11
|
class ClassInfo
|
12
|
+
# Holds an instance to the class you are gathering information from
|
13
13
|
attr_reader :instance
|
14
14
|
|
15
15
|
# Peak into class information
|
@@ -17,24 +17,123 @@ module Peeky
|
|
17
17
|
@instance = instance
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def to_s
|
21
|
+
class_full_name
|
22
|
+
end
|
23
|
+
|
24
|
+
# Load class_info
|
25
|
+
#
|
26
|
+
# Accessing information about methods and parameters is currently
|
27
|
+
# lazy-loaded via memoization.
|
28
|
+
#
|
29
|
+
# At times during debug or other edge cases, it may be useful to
|
30
|
+
# pre-load this information early.
|
31
|
+
def load
|
32
|
+
ruby_instance_methods
|
33
|
+
ruby_instance_method_names
|
34
|
+
signatures
|
35
|
+
end
|
36
|
+
|
37
|
+
# Class full name includes the module namespace
|
38
|
+
def class_full_name
|
21
39
|
instance.class.name
|
22
40
|
end
|
23
41
|
|
42
|
+
# Class name
|
43
|
+
def class_name
|
44
|
+
@_class_name ||= class_full_name.to_s.gsub(/^.*::/, '')
|
45
|
+
# instance.class.name.split('::').last
|
46
|
+
end
|
47
|
+
|
48
|
+
# Module name
|
49
|
+
def module_name
|
50
|
+
@_module_name ||= class_full_name.to_s.gsub(/(.*)::.*/, '\1')
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get a list of :attr_accessor on the class
|
54
|
+
# @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
|
24
55
|
def accessors
|
25
|
-
@
|
56
|
+
@_accessors ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_accessor }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get a list of :attr_accessors ordered the way they are in the source code
|
60
|
+
# @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
|
61
|
+
def accessors_source_order
|
62
|
+
# TODO: This feature is required
|
63
|
+
# May be best to have a sort object that can be created for each type of ordering that is needed
|
64
|
+
accessors
|
26
65
|
end
|
27
66
|
|
67
|
+
# Attribute infos
|
68
|
+
def attribute_infos
|
69
|
+
@_attribute_infos ||= begin
|
70
|
+
grouped_method_infos = signatures.select { |signature| signature.readable? || signature.writable? }.group_by(&:clean_name)
|
71
|
+
|
72
|
+
grouped_method_infos.keys.map { |key| AttrInfo.create(*grouped_method_infos[key]) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Method by name
|
77
|
+
#
|
78
|
+
# @param name [String] name (required)
|
79
|
+
def method_by_name(name)
|
80
|
+
signatures_by_name(name, filter_type: :method).first
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get a list methods
|
84
|
+
# @return [Array<MethodInfo>] list of MethodInfo where type is :method
|
85
|
+
def methods
|
86
|
+
@_methods ||= signatures.select { |signature| signature.implementation_type == :method }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get a list methods ordered the way they are in the source code
|
90
|
+
# @return [Array<MethodInfo>] list of MethodInfo
|
91
|
+
def methods_source_order
|
92
|
+
# TODO: This feature is required
|
93
|
+
# May be best to have a sort object that can be created for each type of ordering that is needed
|
94
|
+
methods
|
95
|
+
end
|
96
|
+
|
97
|
+
# Reader by name
|
98
|
+
#
|
99
|
+
# @param name [String] name (required)
|
100
|
+
def reader_by_name(name)
|
101
|
+
signatures_by_name(name, filter_type: :attr_reader).first
|
102
|
+
end
|
103
|
+
|
104
|
+
# Get a list of :attr_reader on the class
|
105
|
+
# @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
|
28
106
|
def readers
|
29
|
-
@
|
107
|
+
@_readers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_reader }
|
30
108
|
end
|
31
109
|
|
110
|
+
# Get a list of :attr_reader ordered the way they are in the source code
|
111
|
+
# @return [Array<AttrInfo>] list of AttrInfo where type is :attr_reader
|
112
|
+
def readers_source_order
|
113
|
+
# TODO: This feature is required
|
114
|
+
# May be best to have a sort object that can be created for each type of ordering that is needed
|
115
|
+
readers
|
116
|
+
end
|
117
|
+
|
118
|
+
# Get a list of :attr_writer on the class
|
119
|
+
# @return [Array<AttrInfo>] list of AttrInfo where type is :attr_writer
|
32
120
|
def writers
|
33
|
-
@
|
121
|
+
@_writers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_writer }
|
34
122
|
end
|
35
123
|
|
36
|
-
|
37
|
-
|
124
|
+
# Get a list of :attr_writer ordered the way they are in the source code
|
125
|
+
# @return [Array<AttrInfo>] list of AttrInfo where type is :attr_writer
|
126
|
+
def writers_source_order
|
127
|
+
# TODO: This feature is required
|
128
|
+
# May be best to have a sort object that can be created for each type of ordering that is needed
|
129
|
+
writers
|
130
|
+
end
|
131
|
+
|
132
|
+
# Writer by name
|
133
|
+
#
|
134
|
+
# @param name [String] name (required)
|
135
|
+
def writer_by_name(name)
|
136
|
+
signatures_by_name(name, filter_type: :attr_writer).first
|
38
137
|
end
|
39
138
|
|
40
139
|
# def signatures(types = [:instance])
|
@@ -42,36 +141,36 @@ module Peeky
|
|
42
141
|
# such as static, private vs public
|
43
142
|
# deep, deep_to_level, this_instance.
|
44
143
|
def signatures
|
45
|
-
@
|
46
|
-
end
|
47
|
-
|
48
|
-
def signatures_by_name(name)
|
49
|
-
signatures.select { |im| im.name == name }
|
144
|
+
@_signatures ||= ruby_instance_methods.map { |im| MethodInfo.new(im, @instance) }
|
50
145
|
end
|
51
146
|
|
147
|
+
# Signatures by clean name
|
148
|
+
#
|
149
|
+
# @param clean_name [String] clean name (required)
|
52
150
|
def signatures_by_clean_name(clean_name)
|
53
151
|
signatures.select { |im| im.clean_name == clean_name }
|
54
152
|
end
|
55
153
|
|
56
|
-
|
57
|
-
|
58
|
-
|
154
|
+
# Signatures by name
|
155
|
+
#
|
156
|
+
# @param name [String] name (required)
|
157
|
+
# @param filter_type [String] filter_type: <value for filter type> (optional)
|
158
|
+
def signatures_by_name(name, filter_type: :all)
|
159
|
+
return signatures.select { |im| im.name == name } if filter_type == :all
|
59
160
|
|
60
|
-
|
61
|
-
end
|
161
|
+
signatures.select { |im| im.name == name && im.implementation_type == filter_type }
|
62
162
|
end
|
63
163
|
|
164
|
+
# Debug
|
165
|
+
#
|
166
|
+
# Refact: PATTERN: Come up it an debug inclusion system so that
|
167
|
+
# so that debug helpers can be included for development and excluded
|
168
|
+
# for production
|
169
|
+
# @param format [String] format: <value for format> (optional)
|
64
170
|
def debug(format: [:signatures])
|
65
|
-
if format
|
66
|
-
puts '-' * 70
|
67
|
-
puts 'Method Names'
|
68
|
-
puts '-' * 70
|
69
|
-
ruby_instance_method_names.each do |method_name|
|
70
|
-
puts method_name
|
71
|
-
end
|
72
|
-
end
|
171
|
+
debug_method_names if format.include?(:method_names)
|
73
172
|
|
74
|
-
return unless format
|
173
|
+
return unless format.include?(:signatures)
|
75
174
|
|
76
175
|
puts '-' * 70
|
77
176
|
puts 'Methods'
|
@@ -79,14 +178,25 @@ module Peeky
|
|
79
178
|
signatures.each(&:debug)
|
80
179
|
end
|
81
180
|
|
181
|
+
def debug_method_names
|
182
|
+
puts '-' * 70
|
183
|
+
puts 'Method Names'
|
184
|
+
puts '-' * 70
|
185
|
+
ruby_instance_method_names.each do |method_name|
|
186
|
+
puts method_name
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
82
190
|
private
|
83
191
|
|
84
192
|
def ruby_instance_method_names
|
85
|
-
@
|
193
|
+
@_ruby_instance_method_names ||= instance.class.instance_methods(false).sort
|
86
194
|
end
|
87
195
|
|
88
196
|
def ruby_instance_methods
|
89
|
-
@
|
197
|
+
@_ruby_instance_methods ||= ruby_instance_method_names.map { |method_name| instance.method(method_name) }
|
198
|
+
rescue StandardError => e
|
199
|
+
puts e
|
90
200
|
end
|
91
201
|
end
|
92
202
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Peeky
|
2
|
+
module Example
|
3
|
+
# Yard sample
|
4
|
+
class YardSample
|
5
|
+
# A read write1
|
6
|
+
attr_accessor :a_read_write1
|
7
|
+
|
8
|
+
# A read write2
|
9
|
+
attr_accessor :a_read_write2
|
10
|
+
|
11
|
+
# A read write3
|
12
|
+
attr_accessor :a_read_write3
|
13
|
+
|
14
|
+
# B reader1
|
15
|
+
attr_reader :b_reader1
|
16
|
+
|
17
|
+
# B reader2
|
18
|
+
attr_reader :b_reader2
|
19
|
+
|
20
|
+
# E looks like an attr reader
|
21
|
+
attr_reader :e_looks_like_an_attr_reader
|
22
|
+
|
23
|
+
# C writer1
|
24
|
+
attr_writer :c_writer1
|
25
|
+
|
26
|
+
# C writer2
|
27
|
+
attr_writer :c_writer2
|
28
|
+
|
29
|
+
# Alpha sort1
|
30
|
+
def alpha_sort1
|
31
|
+
end
|
32
|
+
|
33
|
+
# Alpha sort2
|
34
|
+
def alpha_sort2
|
35
|
+
end
|
36
|
+
|
37
|
+
# D do something method
|
38
|
+
def d_do_something_method
|
39
|
+
end
|
40
|
+
|
41
|
+
# E method with required param
|
42
|
+
#
|
43
|
+
# @param first_name [String] first name (required)
|
44
|
+
def e_method_with_required_param(first_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
# F method with required param and optional param
|
48
|
+
#
|
49
|
+
# @param first_name [String] first name (required)
|
50
|
+
# @param last_name [String] last name (optional)
|
51
|
+
def f_method_with_required_param_and_optional_param(first_name, last_name = nil)
|
52
|
+
end
|
53
|
+
|
54
|
+
# G method with required param and two optional params
|
55
|
+
#
|
56
|
+
# @param first_name [String] first name (required)
|
57
|
+
# @param last_name [String] last name (optional)
|
58
|
+
# @param age [String] age (optional)
|
59
|
+
def g_method_with_required_param_and_two_optional_params(first_name, last_name = nil, age = nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
# H list of optional parameters
|
63
|
+
#
|
64
|
+
# @param command_args [Array<Object>] *command_args - list of command args
|
65
|
+
def h_list_of_optional_parameters(*command_args)
|
66
|
+
end
|
67
|
+
|
68
|
+
# I method with two required params and list of optional params
|
69
|
+
#
|
70
|
+
# @param first_name [String] first name (required)
|
71
|
+
# @param last_name [String] last name (required)
|
72
|
+
# @param alias_names [Array<Object>] *alias_names - list of alias names
|
73
|
+
def i_method_with_two_required_params_and_list_of_optional_params(first_name, last_name, *alias_names)
|
74
|
+
end
|
75
|
+
|
76
|
+
# J method with list of named arguments
|
77
|
+
#
|
78
|
+
# @param options [<key: value>...] **options - list of key/values
|
79
|
+
def j_method_with_list_of_named_arguments(**options)
|
80
|
+
end
|
81
|
+
|
82
|
+
# K method with block
|
83
|
+
#
|
84
|
+
# @param code_block [Block] &code_block
|
85
|
+
def k_method_with_block(&code_block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# L method with key value param required
|
89
|
+
#
|
90
|
+
# @param name [String] name: <value for name> (required)
|
91
|
+
def l_method_with_key_value_param_required(name:)
|
92
|
+
end
|
93
|
+
|
94
|
+
# N method with key value param required and optional key value
|
95
|
+
#
|
96
|
+
# @param last_name [String] last_name: <value for last name> (required)
|
97
|
+
# @param salutation [String] salutation: <value for salutation> (optional)
|
98
|
+
def n_method_with_key_value_param_required_and_optional_key_value(last_name:, salutation: nil)
|
99
|
+
end
|
100
|
+
|
101
|
+
# P available?
|
102
|
+
|
103
|
+
# @return [Boolean] true when p available?
|
104
|
+
def p_available?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Q danger will robinson!
|
108
|
+
def q_danger_will_robinson!
|
109
|
+
end
|
110
|
+
|
111
|
+
# Z complex
|
112
|
+
#
|
113
|
+
# @param aaa [String] aaa (required)
|
114
|
+
# @param bbb [String] bbb (optional)
|
115
|
+
# @param ccc [Array<Object>] *ccc - list of ccc
|
116
|
+
# @param ddd [String] ddd: <value for ddd> (required)
|
117
|
+
# @param eee [String] eee: <value for eee> (optional)
|
118
|
+
# @param fff [<key: value>...] **fff - list of key/values
|
119
|
+
# @param ggg [Block] &ggg
|
120
|
+
def z_complex(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/peeky/method_info.rb
CHANGED
@@ -12,18 +12,19 @@ module Peeky
|
|
12
12
|
attr_accessor :parameters
|
13
13
|
|
14
14
|
# MethodInfo delegates to the underlying ruby method object
|
15
|
-
attr_reader :
|
15
|
+
attr_reader :focal_method
|
16
16
|
|
17
|
-
def_delegators :
|
17
|
+
def_delegators :focal_method, :name, :receiver, :arity, :super_method
|
18
18
|
|
19
19
|
# Stage 2 is the method likely to be an attribute reader or writer
|
20
20
|
|
21
|
-
#
|
21
|
+
# Implementation type indicates the probable representation of this
|
22
22
|
# method in ruby, was it `def method` or `attr_reader` / `attr_writer`
|
23
23
|
attr_reader :implementation_type
|
24
24
|
|
25
|
+
# TODO: target_instance is required...
|
25
26
|
def initialize(method, target_instance = nil)
|
26
|
-
@
|
27
|
+
@focal_method = method
|
27
28
|
@parameters = ParameterInfo.from_method(method)
|
28
29
|
# stage 1
|
29
30
|
# @implementation_type = :method
|