peeky 0.0.14 → 0.0.28
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/.github/workflows/ruby.yml +31 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +19 -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 +12 -32
- data/STORIES.md +83 -0
- data/lib/peeky.rb +3 -0
- data/lib/peeky/api.rb +70 -0
- data/lib/peeky/class_info.rb +133 -29
- data/lib/peeky/example/yard_sample.rb +123 -0
- data/lib/peeky/method_info.rb +7 -5
- 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_interface_render.rb +10 -10
- data/lib/peeky/renderer/class_interface_yard_render.rb +142 -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 +21 -4
- metadata +38 -8
@@ -0,0 +1,123 @@
|
|
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
|
+
# @return [Boolean] true when p available?
|
103
|
+
def p_available?
|
104
|
+
end
|
105
|
+
|
106
|
+
# Q danger will robinson!
|
107
|
+
def q_danger_will_robinson!
|
108
|
+
end
|
109
|
+
|
110
|
+
# Z complex
|
111
|
+
#
|
112
|
+
# @param aaa [String] aaa (required)
|
113
|
+
# @param bbb [String] bbb (optional)
|
114
|
+
# @param ccc [Array<Object>] *ccc - list of ccc
|
115
|
+
# @param ddd [String] ddd: <value for ddd> (required)
|
116
|
+
# @param eee [String] eee: <value for eee> (optional)
|
117
|
+
# @param fff [<key: value>...] **fff - list of key/values
|
118
|
+
# @param ggg [Block] &ggg
|
119
|
+
def z_complex(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/peeky/method_info.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
require 'forwardable'
|
4
4
|
|
5
5
|
module Peeky
|
6
|
-
# Method
|
6
|
+
# Method info store a list of instance methods and attr_* for
|
7
|
+
# a ruby class.
|
7
8
|
class MethodInfo
|
8
9
|
extend Forwardable
|
9
10
|
|
@@ -11,18 +12,19 @@ module Peeky
|
|
11
12
|
attr_accessor :parameters
|
12
13
|
|
13
14
|
# MethodInfo delegates to the underlying ruby method object
|
14
|
-
attr_reader :
|
15
|
+
attr_reader :focal_method
|
15
16
|
|
16
|
-
def_delegators :
|
17
|
+
def_delegators :focal_method, :name, :receiver, :arity, :super_method
|
17
18
|
|
18
19
|
# Stage 2 is the method likely to be an attribute reader or writer
|
19
20
|
|
20
|
-
#
|
21
|
+
# Implementation type indicates the probable representation of this
|
21
22
|
# method in ruby, was it `def method` or `attr_reader` / `attr_writer`
|
22
23
|
attr_reader :implementation_type
|
23
24
|
|
25
|
+
# TODO: target_instance is required...
|
24
26
|
def initialize(method, target_instance = nil)
|
25
|
-
@
|
27
|
+
@focal_method = method
|
26
28
|
@parameters = ParameterInfo.from_method(method)
|
27
29
|
# stage 1
|
28
30
|
# @implementation_type = :method
|
@@ -7,27 +7,39 @@ module Peeky
|
|
7
7
|
# Attr Reader Predicate will match true if the method info could be considered
|
8
8
|
# a valid attr_reader
|
9
9
|
class AttrReaderPredicate
|
10
|
+
# Match will return true if the method_info seems to be an :attr_reader
|
11
|
+
#
|
12
|
+
# @param instance [Object] instance the object that has this method (required)
|
13
|
+
# @param method_info [String] method info (required)
|
10
14
|
def match(instance, method_info)
|
11
15
|
return false unless prerequisites(instance, method_info)
|
12
16
|
|
13
|
-
variable_name = "@#{method_info.name}"
|
14
17
|
method_name = method_info.name
|
15
18
|
|
16
19
|
# Refactor: Fragile
|
17
20
|
# Really need to handle exceptions and types better
|
18
21
|
# old_value = instance.send(method_name)
|
22
|
+
|
23
|
+
# This code works by
|
24
|
+
# 1. Set @name_of_method variable to random value
|
25
|
+
# 2. Call method name and see if it returns that value
|
26
|
+
# 3. Return match<true> if the values are equal
|
19
27
|
new_value = SecureRandom.alphanumeric(20)
|
20
28
|
code = <<-RUBY
|
21
|
-
|
29
|
+
@#{method_name} = '#{new_value}' # eg. @variable = 'a3bj7a3bj7a3bj7a3bj7'
|
22
30
|
RUBY
|
23
|
-
|
24
|
-
|
31
|
+
|
32
|
+
cloned = instance.clone
|
33
|
+
|
34
|
+
cloned.instance_eval(code)
|
35
|
+
current_value = cloned.send(method_name)
|
25
36
|
current_value == new_value
|
26
37
|
end
|
27
38
|
|
28
39
|
private
|
29
40
|
|
30
41
|
def prerequisites(instance, method_info)
|
42
|
+
# look for obvious NON :attr_reader patterns
|
31
43
|
return false if %w[! ? =].include?(method_info.name.to_s[-1..-1])
|
32
44
|
return false unless method_info.parameters.length.zero?
|
33
45
|
return false unless instance.respond_to?(method_info.name)
|
@@ -7,16 +7,24 @@ module Peeky
|
|
7
7
|
# Attr Writer Predicate will match true if the method info could be considered
|
8
8
|
# a valid attr_writer
|
9
9
|
class AttrWriterPredicate
|
10
|
+
# Match will return true if the method_info seems to be an :attr_writer
|
11
|
+
#
|
12
|
+
# @param instance [Object] instance the object that has this method (required)
|
13
|
+
# @param method_info [String] method info (required)
|
10
14
|
def match(instance, method_info)
|
11
15
|
return false unless prerequisites(instance, method_info)
|
12
16
|
|
13
17
|
param = method_info.parameters.first
|
18
|
+
# Taking advantage of an odd reflection concept in ruby where by
|
19
|
+
# method.parameters returns this array value [:req] for :attr_writer
|
20
|
+
# while ordinary methods return [:req, some_param_name]
|
14
21
|
param.type == :param_required && param.name.empty?
|
15
22
|
end
|
16
23
|
|
17
24
|
private
|
18
25
|
|
19
26
|
def prerequisites(instance, method_info)
|
27
|
+
# look for obvious NON :attr_writer patterns
|
20
28
|
return false if %w[! ?].include?(method_info.name.to_s[-1..-1])
|
21
29
|
return false unless method_info.name.to_s.end_with?('=')
|
22
30
|
return false unless instance.respond_to?(method_info.name)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Peeky
|
4
4
|
module Renderer
|
5
|
-
# Class Interface
|
5
|
+
# Render: Class Interface
|
6
6
|
#
|
7
7
|
# Example output:
|
8
8
|
# class SampleClassClassInterfaceRender
|
@@ -30,12 +30,14 @@ module Peeky
|
|
30
30
|
# def z(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg); end
|
31
31
|
# end
|
32
32
|
class ClassInterfaceRender
|
33
|
+
# ClassInfo with information about the class instance to be rendered.
|
33
34
|
attr_reader :class_info
|
34
35
|
|
35
36
|
def initialize(class_info)
|
36
37
|
@class_info = class_info
|
37
38
|
end
|
38
39
|
|
40
|
+
# Render the class interface
|
39
41
|
def render
|
40
42
|
@indent = ''
|
41
43
|
output = []
|
@@ -53,30 +55,32 @@ module Peeky
|
|
53
55
|
output.join("\n")
|
54
56
|
end
|
55
57
|
|
58
|
+
private
|
59
|
+
|
56
60
|
def render_start
|
57
|
-
"#{@indent}class #{class_info.class_name}"
|
61
|
+
"#{@indent}class #{@class_info.class_name}"
|
58
62
|
end
|
59
63
|
|
60
64
|
def render_accessors
|
61
|
-
result = class_info.accessors.map { |attr| "#{@indent}attr_accessor :#{attr.name}" }
|
65
|
+
result = @class_info.accessors.map { |attr| "#{@indent}attr_accessor :#{attr.name}" }
|
62
66
|
result.push '' unless result.length.zero?
|
63
67
|
result
|
64
68
|
end
|
65
69
|
|
66
70
|
def render_readers
|
67
|
-
result = class_info.readers.map { |attr| "#{@indent}attr_reader :#{attr.name}" }
|
71
|
+
result = @class_info.readers.map { |attr| "#{@indent}attr_reader :#{attr.name}" }
|
68
72
|
result.push '' unless result.length.zero?
|
69
73
|
result
|
70
74
|
end
|
71
75
|
|
72
76
|
def render_writers
|
73
|
-
result = class_info.writers.map { |attr| "#{@indent}attr_writer :#{attr.name}" }
|
77
|
+
result = @class_info.writers.map { |attr| "#{@indent}attr_writer :#{attr.name}" }
|
74
78
|
result.push '' unless result.length.zero?
|
75
79
|
result
|
76
80
|
end
|
77
81
|
|
78
82
|
def render_methods
|
79
|
-
result = class_info.methods.map do |method_signature|
|
83
|
+
result = @class_info.methods.map do |method_signature|
|
80
84
|
render_signature = Peeky::Renderer::MethodSignatureRender.new(method_signature)
|
81
85
|
"#{@indent}#{render_signature.render}"
|
82
86
|
end
|
@@ -87,10 +91,6 @@ module Peeky
|
|
87
91
|
def render_end
|
88
92
|
"#{@indent}end"
|
89
93
|
end
|
90
|
-
|
91
|
-
def debug
|
92
|
-
puts render
|
93
|
-
end
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'active_support/core_ext'
|
4
|
+
require 'active_support/core_ext/string'
|
5
|
+
|
6
|
+
module Peeky
|
7
|
+
module Renderer
|
8
|
+
# Render: Class Interface with YARD documentation
|
9
|
+
class ClassInterfaceYardRender
|
10
|
+
# Indentation prefix as a string, defaults to +''+
|
11
|
+
#
|
12
|
+
# If you were writing a class into a file with an existing
|
13
|
+
# module, you may set the indent to +' '+ if you wanted this
|
14
|
+
# render to indent by two spaces
|
15
|
+
attr_accessor :indent
|
16
|
+
|
17
|
+
# Default param type when documenting positional and named parameters.
|
18
|
+
# Defaults to <String>
|
19
|
+
attr_accessor :default_param_type
|
20
|
+
|
21
|
+
# Default param type when documenting splat *parameters.
|
22
|
+
# Defaults to <Object>
|
23
|
+
attr_accessor :default_splat_param_type
|
24
|
+
|
25
|
+
# ClassInfo with information about the class instance to be rendered.
|
26
|
+
attr_reader :class_info
|
27
|
+
|
28
|
+
def initialize(class_info)
|
29
|
+
@class_info = class_info
|
30
|
+
@indent = ''
|
31
|
+
@default_param_type = 'String'
|
32
|
+
@default_splat_param_type = 'Object'
|
33
|
+
end
|
34
|
+
|
35
|
+
# Render the class interface with YARD documentation
|
36
|
+
def render
|
37
|
+
output = []
|
38
|
+
output.push render_start
|
39
|
+
@indent += ' '
|
40
|
+
output += render_accessors
|
41
|
+
output += render_readers
|
42
|
+
output += render_writers
|
43
|
+
output += render_methods
|
44
|
+
output.pop if output.last == ''
|
45
|
+
|
46
|
+
@indent = @indent[0..-3]
|
47
|
+
|
48
|
+
output.push render_end
|
49
|
+
|
50
|
+
output.join("\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def render_start
|
56
|
+
[
|
57
|
+
"#{@indent}# #{@class_info.class_name.titleize.humanize}",
|
58
|
+
"#{@indent}class #{@class_info.class_name}"
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
def render_accessors
|
63
|
+
result = []
|
64
|
+
@class_info.accessors.map.with_index do |attr, index|
|
65
|
+
result.push '' if index.positive?
|
66
|
+
result.push "#{@indent}# #{attr.name.to_s.humanize}"
|
67
|
+
result.push "#{@indent}attr_accessor :#{attr.name}"
|
68
|
+
end
|
69
|
+
result.push '' unless result.length.zero?
|
70
|
+
result
|
71
|
+
end
|
72
|
+
|
73
|
+
def render_readers
|
74
|
+
result = []
|
75
|
+
@class_info.readers.map.with_index do |attr, index|
|
76
|
+
result.push '' if index.positive?
|
77
|
+
result.push "#{@indent}# #{attr.name.to_s.humanize}"
|
78
|
+
result.push "#{@indent}attr_reader :#{attr.name}"
|
79
|
+
end
|
80
|
+
result.push '' unless result.length.zero?
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
def render_writers
|
85
|
+
result = []
|
86
|
+
class_info.writers.map.with_index do |attr, index|
|
87
|
+
result.push '' if index.positive?
|
88
|
+
result.push "#{@indent}# #{attr.name.to_s.humanize}"
|
89
|
+
result.push "#{@indent}attr_writer :#{attr.name}"
|
90
|
+
end
|
91
|
+
result.push '' unless result.length.zero?
|
92
|
+
result
|
93
|
+
end
|
94
|
+
|
95
|
+
# rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
96
|
+
def render_methods
|
97
|
+
result = []
|
98
|
+
class_info.methods.map.with_index do |method_signature, index|
|
99
|
+
result.push '' if index.positive?
|
100
|
+
result.push "#{@indent}# #{method_signature.name.to_s.humanize}"
|
101
|
+
|
102
|
+
method_signature.parameters.each_with_index do |parameter, param_index|
|
103
|
+
result.push "#{@indent}#" if param_index.zero?
|
104
|
+
|
105
|
+
case parameter.type
|
106
|
+
when :splat
|
107
|
+
result.push "#{@indent}# @param #{parameter.name} [Array<#{default_splat_param_type}>] *#{parameter.name} - list of #{parameter.name.to_s.humanize.downcase}"
|
108
|
+
when :double_splat
|
109
|
+
result.push "#{@indent}# @param #{parameter.name} [<key: value>...] **#{parameter.name} - list of key/values"
|
110
|
+
when :block
|
111
|
+
result.push "#{@indent}# @param #{parameter.name} [Block] &#{parameter.name}"
|
112
|
+
when :key_required
|
113
|
+
result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name}: <value for #{parameter.name.to_s.humanize.downcase}> (required)"
|
114
|
+
when :key_optional
|
115
|
+
result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name}: <value for #{parameter.name.to_s.humanize.downcase}> (optional)"
|
116
|
+
when :param_required
|
117
|
+
result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase} (required)"
|
118
|
+
when :param_optional
|
119
|
+
result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase} (optional)"
|
120
|
+
else
|
121
|
+
result.push "#{@indent}# @param #{parameter.name} [#{default_param_type}] #{parameter.name.to_s.humanize.downcase}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
result.push "#{@indent}# @return [Boolean] true when #{method_signature.name.to_s.humanize.downcase}" if method_signature.name.to_s.end_with?('?')
|
126
|
+
|
127
|
+
render_signature = Peeky::Renderer::MethodSignatureRender.new(method_signature)
|
128
|
+
render_signature.indent = @indent
|
129
|
+
render_signature.style = :default
|
130
|
+
result.push render_signature.render
|
131
|
+
end
|
132
|
+
result.push '' unless result.length.zero?
|
133
|
+
result
|
134
|
+
end
|
135
|
+
# rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
136
|
+
|
137
|
+
def render_end
|
138
|
+
"#{@indent}end"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -8,13 +8,16 @@ module Peeky
|
|
8
8
|
# instance.simple('first_param')
|
9
9
|
# instance.complex('aaa', ddd: 'ddd')
|
10
10
|
class MethodCallMinimumParamsRender
|
11
|
+
# Method signature stores a MethodInfo object
|
11
12
|
attr_reader :method_signature
|
12
13
|
|
13
|
-
def initialize(method_signature,
|
14
|
+
def initialize(method_signature, **opts)
|
15
|
+
instance_name = opts[:instance_name] || 'instance'
|
14
16
|
@instance_name = instance_name
|
15
17
|
@method_signature = method_signature
|
16
18
|
end
|
17
19
|
|
20
|
+
# Render the a method call with minimal parameters
|
18
21
|
def render
|
19
22
|
name = method_signature.name
|
20
23
|
|
@@ -28,10 +31,6 @@ module Peeky
|
|
28
31
|
|
29
32
|
"#{@instance_name}.#{name}#{params}"
|
30
33
|
end
|
31
|
-
|
32
|
-
def debug
|
33
|
-
puts render
|
34
|
-
end
|
35
34
|
end
|
36
35
|
end
|
37
36
|
end
|