peeky 0.0.19 → 0.0.33
Sign up to get free protection for your applications and to get access to all the features.
- 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
|