peeky 0.0.19 → 0.0.25

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17aca44d1863e4d7d811c2fdf09bbe4aec22c8aa5a2d0d09983a8d9c50f14430
4
- data.tar.gz: ff605fe00998f4e7ca15725f20171180329edd882256c46eaa20c96d41491488
3
+ metadata.gz: 75fed761de619d444f8e84f4e08df5df41e9f12b89e95051b60dd0f60bafa57e
4
+ data.tar.gz: 05e1ba1ec76ffe0689f536e8e2c78c86fd16570051212ca9031ef3cf5f302e9b
5
5
  SHA512:
6
- metadata.gz: e212ec6b8cb3e88e78e57f42d7a880aabf73e46cabd0b23e8d3ce93a0819a74d36f7dbf1d1af95f2677824aa885e277d003445c4d8bc656afd4e1245f8cd8d04
7
- data.tar.gz: 3498282a3c71032ef6866783d06f642b7c834079d3cfae164d577a734ba5194c082b820b956bef116f7277fed19993f7833776e3fe97c1401d1485cf976e176c
6
+ metadata.gz: 19bb5a15dc9c0db1a5346632f448da2ff7396f247c474a89b1dee7c2097e1fdf824bce8227a35762bc85db49d4301463f613f78999a20f1656edb870e4eabb51
7
+ data.tar.gz: 89ae53bd7a7a840a92214337bc7a08c5e813eab892a94391cb69b2e27a0ad87546c7489f03c710f20328b770abafd3ccf45c92a5bdaf22ed0d5ffb2b93fcd2cf
@@ -4,8 +4,23 @@ AllCops:
4
4
  NewCops: enable
5
5
  Exclude:
6
6
  - "_/**/*"
7
+ - "lib/peeky/example/yard_sample.rb"
7
8
 
8
9
  # My Preferences - Start
10
+ Style/EmptyMethod:
11
+ Exclude:
12
+ - "**/spec/**/*"
13
+ Metrics/ParameterLists:
14
+ Exclude:
15
+ - "**/spec/**/*"
16
+ Layout/EmptyLineBetweenDefs:
17
+ Exclude:
18
+ - "**/spec/**/*"
19
+
20
+ Lint/AmbiguousBlockAssociation:
21
+ Exclude:
22
+ - "**/spec/**/*"
23
+
9
24
  Style/AccessorGrouping:
10
25
  Enabled: false
11
26
 
@@ -16,6 +31,7 @@ Layout/SpaceBeforeComma:
16
31
  Metrics/BlockLength:
17
32
  Exclude:
18
33
  - "**/spec/*"
34
+ - "*.gemspec"
19
35
  ExcludedMethods:
20
36
  - configure
21
37
  - context
@@ -21,6 +21,10 @@ Gemspec/RequiredRubyVersion:
21
21
  Exclude:
22
22
  - 'peeky.gemspec'
23
23
 
24
+ Metrics/BlockLength:
25
+ Exclude:
26
+ - 'peeky.gemspec'
27
+
24
28
  # Offense count: 1
25
29
  # Cop supports --auto-correct.
26
30
  # Configuration parameters: EnforcedStyle.
data/Gemfile CHANGED
@@ -5,13 +5,16 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in poc_github_ap.gemspec
6
6
  gemspec
7
7
 
8
+ group :development do
9
+ # pry on steroids
10
+ gem 'pry-coolline', github: 'owst/pry-coolline', branch: 'support_new_pry_config_api'
11
+ gem 'jazz_fingers'
12
+ end
13
+
8
14
  group :development, :test do
9
15
  gem 'guard-bundler'
10
16
  gem 'guard-rspec'
11
17
  gem 'guard-rubocop'
12
- # pry on steroids
13
- # gem 'pry-coolline', github: 'owst/pry-coolline', branch: 'support_new_pry_config_api'
14
- gem 'jazz_fingers'
15
18
  gem 'rake', '~> 12.0'
16
19
  # this is used for cmdlets 'self-executing gems'
17
20
  gem 'rake-compiler'
data/Guardfile CHANGED
@@ -23,8 +23,8 @@ group :green_pass_then_cop, halt_on_fail: true do
23
23
  watch(%r{^lib/peeky/commands/(.+)\.rb$}) { |m| "spec/unit/commands/#{m[1]}_spec.rb" }
24
24
  end
25
25
 
26
- guard :rubocop, all_on_start: false, cli: ['--format', 'clang'] do
27
- watch(%r{.+\.rb$})
28
- watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
29
- end
26
+ # guard :rubocop, all_on_start: false, cli: ['--format', 'clang'] do
27
+ # watch(%r{.+\.rb$})
28
+ # watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
29
+ # end
30
30
  end
@@ -4,7 +4,37 @@
4
4
 
5
5
  As a Ruby Developer, I should be able to Reverse engineer classes and methods, so that I can document and understand them
6
6
 
7
- ## Stories
7
+ ## Development rader
8
+
9
+
10
+ ### Tasks next on list
11
+
12
+ As a Developer, I can render a class with RDoc documentation, so that I do not have to manually type RDoc references
13
+
14
+ - Add simplified API with examples
15
+ - Start documenting usage instructions
16
+
17
+ As a Developer, I can use Peeky with a simple API, so that I use this GEM quickly
18
+
19
+ - Add simplified API with examples
20
+ - Start documenting usage instructions
21
+
22
+ As a Developer, I can quickly build requirements, so that I can document project features
23
+
24
+ - Add support for backlog stories and tasks
25
+ - Add usage instructions
26
+
27
+
28
+
29
+ ## Stories and tasks
30
+
31
+ ### Stories - completed
32
+
33
+ As a David, I can edify Karin, because she is cool
34
+
35
+ - karin_is_awesome
36
+ - i_am_very_grateful_for_karin
37
+ - karin_the_beautiful
8
38
 
9
39
  As a Developer, I can render a class with instance attributes and methods, So that I can quickly mock out an entire class
10
40
 
@@ -35,15 +65,13 @@ As a Developer, I should be able to interrogate class instance information, so t
35
65
  - ClassInfo stores information about a ruby class. Only support instance methods
36
66
 
37
67
 
38
- ## Tasks
39
-
68
+ ##$ Tasks - completed
40
69
 
41
70
  Setup GitHub Action (test and lint)
42
71
 
43
72
  - Setup Rspec action
44
73
  - Setup RuboCop action
45
74
 
46
-
47
75
  Setup new Ruby GEM
48
76
 
49
77
  - Build out a standard GEM structure
data/README.md CHANGED
@@ -2,12 +2,8 @@
2
2
 
3
3
  > Peeky is a Ruby GEM for peaking into ruby classes and extracting meta
4
4
 
5
- Welcome to your new ruby gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/peeky`.
6
-
7
5
  When using the source code for this gem, start by running `bin/setup` to install locally or `bundle install`
8
6
 
9
- To experiment with that code, run `bin/console` for an interactive prompt or run `exe/peeky` to see a list of commands.
10
-
11
7
  ## Installation
12
8
 
13
9
  Add this line to your application's Gemfile:
@@ -62,7 +58,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
62
58
 
63
59
  ## Code of Conduct
64
60
 
65
- Everyone interacting in the Peeky project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/peeky/blob/master/CODE_OF_CONDUCT.md).
61
+ Everyone interacting in the Peeky project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the.
66
62
 
67
63
  ## Copyright
68
64
 
@@ -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'
@@ -11,6 +13,7 @@ require 'peeky/predicates/attr_reader_predicate'
11
13
  require 'peeky/predicates/attr_writer_predicate'
12
14
 
13
15
  require 'peeky/renderer/class_interface_render'
16
+ require 'peeky/renderer/class_interface_yard_render'
14
17
  require 'peeky/renderer/method_call_minimum_params_render'
15
18
  require 'peeky/renderer/method_signature_render'
16
19
  require 'peeky/renderer/method_signature_with_debug_render'
@@ -0,0 +1,70 @@
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)
20
+ Peeky::ClassInfo.new(instance)
21
+ end
22
+
23
+ # Render a class using a predefined class renderer
24
+ def render_class(render_key, class_info: nil, instance: nil, **_opts)
25
+ raise 'Call render_class with class_info OR instance.' if class_info.nil? && instance.nil?
26
+ raise 'Call render_class with class_info OR instance, these parameters are mutually exclusive' if !class_info.nil? && !instance.nil?
27
+
28
+ renderer = class_renderer(render_key)
29
+
30
+ class_info = Peeky::ClassInfo.new(instance) if class_info.nil?
31
+
32
+ renderer.new(class_info).render
33
+ end
34
+
35
+ # Get a method renderer by :key
36
+ #
37
+ # TODO: Refactor to a configurable system
38
+ def method_renderer(key)
39
+ case key
40
+ when :signature
41
+ Peeky::Renderer::MethodSignatureRender
42
+ when :signature_with_debug
43
+ Peeky::Renderer::MethodSignatureWithDebugRender
44
+ when :call_minimum_params
45
+ Peeky::Renderer::MethodCallMinimumParamsRender
46
+ else
47
+ raise "Unknown method renderer: #{key}"
48
+ end
49
+ end
50
+
51
+ # Get a class renderer by :key
52
+ #
53
+ # TODO: Refactor to a configurable system
54
+ def class_renderer(key)
55
+ case key
56
+ when :class_interface
57
+ Peeky::Renderer::ClassInterfaceRender
58
+ when :class_interface_yard
59
+ Peeky::Renderer::ClassInterfaceYardRender
60
+ else
61
+ raise "Unknown class renderer: #{key}"
62
+ end
63
+ end
64
+
65
+ # def render_method()
66
+ # end
67
+ end
68
+ end
69
+
70
+ Peeky.api = Peeky::Api.new
@@ -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,106 @@ module Peeky
17
17
  @instance = instance
18
18
  end
19
19
 
20
- def class_name
20
+ # Class full name includes the module namespace
21
+ def class_full_name
21
22
  instance.class.name
22
23
  end
23
24
 
25
+ # Class name
26
+ def class_name
27
+ @_class_name ||= class_full_name.to_s.gsub(/^.*::/, '')
28
+ # instance.class.name.split('::').last
29
+ end
30
+
31
+ # Module name
32
+ def module_name
33
+ @_module_name ||= class_full_name.to_s.gsub(/(.*)::.*/, '\1')
34
+ end
35
+
36
+ # Get a list of :attr_accessor on the class
37
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
24
38
  def accessors
25
- @accessors ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_accessor }
39
+ @_accessors ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_accessor }
40
+ end
41
+
42
+ # Get a list of :attr_accessors ordered the way they are in the source code
43
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
44
+ def accessors_source_order
45
+ # TODO: This feature is required
46
+ # May be best to have a sort object that can be created for each type of ordering that is needed
47
+ accessors
48
+ end
49
+
50
+ # Attribute infos
51
+ def attribute_infos
52
+ @_attribute_infos ||= begin
53
+ grouped_method_infos = signatures.select { |signature| signature.readable? || signature.writable? }.group_by(&:clean_name)
54
+
55
+ grouped_method_infos.keys.map { |key| AttrInfo.create(*grouped_method_infos[key]) }
56
+ end
57
+ end
58
+
59
+ # Method by name
60
+ #
61
+ # @param name [String] name (required)
62
+ def method_by_name(name)
63
+ signatures_by_name(name, filter_type: :method).first
64
+ end
65
+
66
+ # Get a list methods
67
+ # @return [Array<MethodInfo>] list of MethodInfo where type is :method
68
+ def methods
69
+ @_methods ||= signatures.select { |signature| signature.implementation_type == :method }
70
+ end
71
+
72
+ # Get a list methods ordered the way they are in the source code
73
+ # @return [Array<MethodInfo>] list of MethodInfo
74
+ def methods_source_order
75
+ # TODO: This feature is required
76
+ # May be best to have a sort object that can be created for each type of ordering that is needed
77
+ methods
78
+ end
79
+
80
+ # Reader by name
81
+ #
82
+ # @param name [String] name (required)
83
+ def reader_by_name(name)
84
+ signatures_by_name(name, filter_type: :attr_reader).first
26
85
  end
27
86
 
87
+ # Get a list of :attr_reader on the class
88
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_accessor
28
89
  def readers
29
- @readers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_reader }
90
+ @_readers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_reader }
30
91
  end
31
92
 
93
+ # Get a list of :attr_reader ordered the way they are in the source code
94
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_reader
95
+ def readers_source_order
96
+ # TODO: This feature is required
97
+ # May be best to have a sort object that can be created for each type of ordering that is needed
98
+ readers
99
+ end
100
+
101
+ # Get a list of :attr_writer on the class
102
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_writer
32
103
  def writers
33
- @writers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_writer }
104
+ @_writers ||= attribute_infos.select { |attribute_info| attribute_info.type == :attr_writer }
34
105
  end
35
106
 
36
- def methods
37
- @methods ||= signatures.select { |signature| signature.implementation_type == :method }
107
+ # Get a list of :attr_writer ordered the way they are in the source code
108
+ # @return [Array<AttrInfo>] list of AttrInfo where type is :attr_writer
109
+ def writers_source_order
110
+ # TODO: This feature is required
111
+ # May be best to have a sort object that can be created for each type of ordering that is needed
112
+ writers
113
+ end
114
+
115
+ # Writer by name
116
+ #
117
+ # @param name [String] name (required)
118
+ def writer_by_name(name)
119
+ signatures_by_name(name, filter_type: :attr_writer).first
38
120
  end
39
121
 
40
122
  # def signatures(types = [:instance])
@@ -42,27 +124,45 @@ module Peeky
42
124
  # such as static, private vs public
43
125
  # deep, deep_to_level, this_instance.
44
126
  def signatures
45
- @signatures ||= ruby_instance_methods.map { |im| MethodInfo.new(im, @instance) }
46
- end
47
-
48
- def signatures_by_name(name)
49
- signatures.select { |im| im.name == name }
127
+ @_signatures ||= ruby_instance_methods.map { |im| MethodInfo.new(im, @instance) }
50
128
  end
51
129
 
130
+ # Signatures by clean name
131
+ #
132
+ # @param clean_name [String] clean name (required)
52
133
  def signatures_by_clean_name(clean_name)
53
134
  signatures.select { |im| im.clean_name == clean_name }
54
135
  end
55
136
 
56
- def attribute_infos
57
- @attribute_infos ||= begin
58
- grouped_method_infos = signatures.select { |signature| signature.readable? || signature.writable? }.group_by(&:clean_name)
137
+ # Signatures by name
138
+ #
139
+ # @param name [String] name (required)
140
+ # @param filter_type [String] filter_type: <value for filter type> (optional)
141
+ def signatures_by_name(name, filter_type: :all)
142
+ return signatures.select { |im| im.name == name } if filter_type == :all
59
143
 
60
- grouped_method_infos.keys.map { |key| AttrInfo.create(*grouped_method_infos[key]) }
61
- end
144
+ signatures.select { |im| im.name == name && im.implementation_type == filter_type }
145
+ end
146
+
147
+ # Build (not sure where I am going with this yet)
148
+ # TODO: Refact: Currently the idea is to pre-load data
149
+ # this is slower when you are not accessing things, but
150
+ # it is easier to debug, so think about what I really want
151
+ # here
152
+ def build()
153
+ ruby_instance_methods
154
+ ruby_instance_method_names
155
+ signatures
62
156
  end
63
157
 
158
+ # Debug
159
+ #
160
+ # Refact: PATTERN: Come up it an debug inclusion system so that
161
+ # so that debug helpers can be included for development and excluded
162
+ # for production
163
+ # @param format [String] format: <value for format> (optional)
64
164
  def debug(format: [:signatures])
65
- if format == :method_names
165
+ if format.include?(:method_names)
66
166
  puts '-' * 70
67
167
  puts 'Method Names'
68
168
  puts '-' * 70
@@ -71,7 +171,7 @@ module Peeky
71
171
  end
72
172
  end
73
173
 
74
- return unless format == :signatures
174
+ return unless format.include?(:signatures)
75
175
 
76
176
  puts '-' * 70
77
177
  puts 'Methods'
@@ -82,11 +182,15 @@ module Peeky
82
182
  private
83
183
 
84
184
  def ruby_instance_method_names
85
- @ruby_instance_method_names ||= instance.class.instance_methods(false).sort
185
+ @_ruby_instance_method_names ||= instance.class.instance_methods(false).sort
86
186
  end
87
187
 
88
188
  def ruby_instance_methods
89
- @ruby_instance_methods ||= ruby_instance_method_names.map { |method_name| instance.method(method_name) }
189
+ begin
190
+ @_ruby_instance_methods ||= ruby_instance_method_names.map { |method_name| instance.method(method_name) }
191
+ rescue => exception
192
+ puts exception
193
+ end
90
194
  end
91
195
  end
92
196
  end
@@ -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
@@ -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 :method
15
+ attr_reader :focal_method
16
16
 
17
- def_delegators :method, :name, :receiver, :arity, :super_method
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
- # Implemented As indicates the probable representation of this
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
- @method = method
27
+ @focal_method = method
27
28
  @parameters = ParameterInfo.from_method(method)
28
29
  # stage 1
29
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
- #{variable_name} = '#{new_value}' # @variable = 'a3bj7a3bj7a3bj7a3bj7'
29
+ @#{method_name} = '#{new_value}' # eg. @variable = 'a3bj7a3bj7a3bj7a3bj7'
22
30
  RUBY
23
- instance.instance_eval(code)
24
- current_value = instance.send(method_name)
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)
@@ -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, instance_name = 'instance')
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
@@ -4,14 +4,54 @@ module Peeky
4
4
  module Renderer
5
5
  # Render: Method Signature in compact format
6
6
  #
7
- # Example output:
7
+ # === Example output (:default):
8
+ # def simple(first_param)
9
+ # end
10
+ #
11
+ # def complex(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg)
12
+ # end
13
+ #
14
+ # === Example output (:compact):
8
15
  # def simple(first_param); end
9
16
  # def complex(aaa, bbb = nil, *ccc, ddd:, eee: nil, **fff, &ggg); end
10
17
  class MethodSignatureRender
18
+ # Indentation prefix as a string.
19
+ #
20
+ # Defaults to ''
21
+ #
22
+ # If you were writing a class into a file with an existing
23
+ # module, you may set the indent to ' ' if you wanted this
24
+ # render to indent by two spaces
25
+ attr_accessor :indent
26
+
27
+ # Style of method rendering [:default, :compact]
28
+ #
29
+ # default:: will render +def method_name+ and +end+ on separate lines
30
+ # compact:: will render +def method_name+ and +end+ on same line
31
+ # @return [Symbol] style
32
+ attr_accessor :style
33
+
34
+ # Method signature stores a MethodInfo object
11
35
  attr_reader :method_signature
12
36
 
13
- def initialize(method_signature)
37
+ def initialize(method_signature, **_opts)
14
38
  @method_signature = method_signature
39
+ @indent = ''
40
+ @style = :compact
41
+ end
42
+
43
+ # Render the method signature in the selected style
44
+ def render
45
+ output = []
46
+ if @style == :compact
47
+ signature = "#{render_signature};"
48
+ output.push "#{signature.ljust(80)}#{render_end}"
49
+ end
50
+ if @style == :default
51
+ output.push render_signature
52
+ output.push render_end
53
+ end
54
+ output.join("\n")
15
55
  end
16
56
 
17
57
  def render_signature
@@ -19,20 +59,11 @@ module Peeky
19
59
 
20
60
  formatted_parameters = method_signature.parameters.map(&:signature_format).join(', ')
21
61
  params = formatted_parameters.length.zero? ? '' : "(#{formatted_parameters})"
22
- "def #{name}#{params}"
62
+ "#{@indent}def #{name}#{params}"
23
63
  end
24
64
 
25
65
  def render_end
26
- 'end'
27
- end
28
-
29
- def render
30
- signature = "#{render_signature};"
31
- "#{signature.ljust(80)}#{render_end}"
32
- end
33
-
34
- def debug
35
- puts render
66
+ "#{@indent}end"
36
67
  end
37
68
  end
38
69
  end
@@ -22,14 +22,22 @@ module Peeky
22
22
  # puts ggg # &ggg is block with many calling options, example - instance_eval(&block) if block_given?
23
23
  # end
24
24
  class MethodSignatureWithDebugRender
25
+ # Method signature stores a MethodInfo object
25
26
  attr_reader :method_signature
26
27
 
27
- def initialize(method_signature)
28
+ def initialize(method_signature, **_opts)
28
29
  @method_signature = method_signature
29
30
 
30
31
  @render_signature = Peeky::Renderer::MethodSignatureRender.new(method_signature)
31
32
  end
32
33
 
34
+ # Render the method with debug statements for each parameter
35
+ def render
36
+ render_method
37
+ end
38
+
39
+ private
40
+
33
41
  def render_method
34
42
  name = method_signature.name
35
43
 
@@ -38,7 +46,7 @@ module Peeky
38
46
  indent = ' '
39
47
  output += "#{indent}puts 'method name: #{name}'\n"
40
48
 
41
- output += render_logic(indent, 30)
49
+ output += render_debug_logic(indent, 30)
42
50
 
43
51
  indent = ''
44
52
  output += "#{indent}#{@render_signature.render_end}\n"
@@ -47,7 +55,7 @@ module Peeky
47
55
  end
48
56
 
49
57
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
50
- def render_logic(indent, size)
58
+ def render_debug_logic(indent, size)
51
59
  output = ''
52
60
  method_signature.parameters.each do |parameter|
53
61
  line = ''
@@ -73,14 +81,6 @@ module Peeky
73
81
  output
74
82
  end
75
83
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
76
-
77
- def render
78
- render_method
79
- end
80
-
81
- def debug
82
- puts render
83
- end
84
84
  end
85
85
  end
86
86
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+
5
+ module Peeky
6
+ module Tools
7
+ # Generate Yard Documentation
8
+ class GenerateYardDocumentation
9
+
10
+
11
+ def initialize
12
+ end
13
+
14
+ def debug
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Peeky
4
- VERSION = '0.0.19'
4
+ VERSION = '0.0.25'
5
5
  end
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
6
6
  spec.required_ruby_version = '>= 2.5'
7
7
  spec.name = 'peeky'
8
8
  spec.version = Peeky::VERSION
9
- spec.authors = ['David C']
9
+ spec.authors = ['David Cruwys']
10
10
  spec.email = ['david@ideasmen.com.au']
11
11
 
12
12
  spec.summary = 'Extracting meta data from ruby classes'
@@ -41,5 +41,6 @@ Gem::Specification.new do |spec|
41
41
  spec.require_paths = ['lib']
42
42
  # spec.extensions = ['ext/peeky/extconf.rb']
43
43
 
44
+ spec.add_dependency 'activesupport'
44
45
  # spec.add_dependency 'tty-box', '~> 0.5.0'
45
46
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: peeky
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.19
4
+ version: 0.0.25
5
5
  platform: ruby
6
6
  authors:
7
- - David C
7
+ - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-03 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-11-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: |2
14
28
  Peeky is a Ruby GEM for peaking into ruby classes and extracting meta data.
15
29
  You can use this meta data to recreate classes, interfaces, documentation etc.
@@ -40,16 +54,20 @@ files:
40
54
  - hooks/pre-commit
41
55
  - hooks/update-version
42
56
  - lib/peeky.rb
57
+ - lib/peeky/api.rb
43
58
  - lib/peeky/attr_info.rb
44
59
  - lib/peeky/class_info.rb
60
+ - lib/peeky/example/yard_sample.rb
45
61
  - lib/peeky/method_info.rb
46
62
  - lib/peeky/parameter_info.rb
47
63
  - lib/peeky/predicates/attr_reader_predicate.rb
48
64
  - lib/peeky/predicates/attr_writer_predicate.rb
49
65
  - lib/peeky/renderer/class_interface_render.rb
66
+ - lib/peeky/renderer/class_interface_yard_render.rb
50
67
  - lib/peeky/renderer/method_call_minimum_params_render.rb
51
68
  - lib/peeky/renderer/method_signature_render.rb
52
69
  - lib/peeky/renderer/method_signature_with_debug_render.rb
70
+ - lib/peeky/tools/generate_yard_documentation.rb
53
71
  - lib/peeky/version.rb
54
72
  - peeky.gemspec
55
73
  homepage: http://appydave.com/gems/peeky