annotable 0.1.2

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.
data/lib/annotable.rb ADDED
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "annotable/annotation"
4
+ require_relative "annotable/method"
5
+ require_relative "annotable/version"
6
+
7
+ #
8
+ # Annotable is a module that can extend any class or module to add the ability to annotate its methods.
9
+ #
10
+ # class Needy
11
+ # extend Annotable # Extend your class or module with Annotable
12
+ # annotable :my_annotation, :my_other_annotation # Declare your annotations
13
+ #
14
+ # my_annotation 42, hello: "world!" # Annotate your methods
15
+ # def method_needing_meta_data
16
+ # # ...
17
+ # end
18
+ #
19
+ # def regular_method
20
+ # # ...
21
+ # end
22
+ # end
23
+ #
24
+ # `Annotable` adds several methods to your class or module, providing access to annotations and their metadata:
25
+ #
26
+ # Needy.annotated_method_exist? :method_needing_meta_data # => true
27
+ # Needy.annotated_method_exist? :regular_method # => false
28
+ # Needy.annotated_methods
29
+ # # => [#<Annotable::Method
30
+ # # @annotations=[
31
+ # # #<Annotable::Annotation
32
+ # # @name=:my_annotation,
33
+ # # @options={:hello=>"world!"},
34
+ # # @params=[42]>
35
+ # # ],
36
+ # # @name=:method_needing_meta_data>
37
+ # # ]
38
+ #
39
+ # `Annotable::Method` represents a method name along with its annotations:
40
+ #
41
+ # method = Needy.annotated_methods.first
42
+ # method.name # => :method_needing_meta_data
43
+ # method.annotations
44
+ # # => [
45
+ # # #<Annotable::Annotation
46
+ # # @name=:my_annotation,
47
+ # # @options={:hello=>"world!"},
48
+ # # @params=[42]>
49
+ # # ]
50
+ #
51
+ # `Annotable::Annotation` contains annotation's name and metadata:
52
+ #
53
+ # annotation = method.annotations.first
54
+ # annotation.name # => :my_annotation
55
+ # annotation.params # => [42]
56
+ # annotation.options # => {:hello => "world!"}
57
+ #
58
+ module Annotable
59
+ #
60
+ # Declares annotations usable in the module or class.
61
+ #
62
+ # annotable :my_annotation, :my_other_annotation
63
+ #
64
+ # This will generate two class methods named after the given symbols.
65
+ # These methods, will push a new `Annotation` in the `current_annotation` array.
66
+ #
67
+ # @param [Array<Symbol>] annotation_names The names of annotations to declare
68
+ #
69
+ # @return [void]
70
+ #
71
+ def annotable(*annotation_names)
72
+ raise ArgumentError, "You must provide at least one annotation name" if annotation_names.empty?
73
+
74
+ annotation_names.each do |name|
75
+ define_singleton_method(name) do |*params, **options|
76
+ current_annotations.push Annotable::Annotation.new(name, params, options)
77
+ end
78
+ end
79
+ end
80
+
81
+ #
82
+ # Return all annotated methods or those matching the given annotation names.
83
+ #
84
+ # @param [Array<Symbol>] names The annotation names to find
85
+ #
86
+ # @return [Array<Annotable::Method>] The annotated methods
87
+ #
88
+ def annotated_methods(*names)
89
+ return @annotated_methods if @annotated_methods.empty? || names.empty?
90
+
91
+ @annotated_methods.select do |method|
92
+ annotation_found = false
93
+
94
+ names.each do |name|
95
+ annotation_found = method.annotation_exist?(name)
96
+ break if annotation_found
97
+ end
98
+
99
+ annotation_found
100
+ end
101
+ end
102
+
103
+ #
104
+ # Check if an annotated method exists based in its name.
105
+ #
106
+ # @param [Symbol] name The name to check
107
+ #
108
+ # @return [Boolean] True if the annotated method exists, false otherwise
109
+ #
110
+ def annotated_method_exist?(name)
111
+ !annotated_methods.find { |am| am.name == name }.nil?
112
+ end
113
+
114
+ private
115
+
116
+ #
117
+ # A callback called by Ruby when a method is added into the class or module.
118
+ #
119
+ # @param [Symbol] name The name of the created method
120
+ #
121
+ # @return [void]
122
+ #
123
+ def method_added(name)
124
+ super
125
+ @annotated_methods ||= []
126
+ return if current_annotations.empty?
127
+
128
+ remove_annotated_method(name) if annotated_method_exist?(name)
129
+ @annotated_methods.push Annotable::Method.new(name, *current_annotations)
130
+
131
+ reset_current_annotations
132
+ end
133
+
134
+ #
135
+ # Remove an annotated method based on its name.
136
+ #
137
+ # @param [Symbol] name The name of the method to delete
138
+ #
139
+ # @return [void]
140
+ #
141
+ def remove_annotated_method(name)
142
+ @annotated_methods.reject! do |annotated_method|
143
+ annotated_method.name == name
144
+ end
145
+ end
146
+
147
+ #
148
+ # Annotation found for the current method declaration.
149
+ #
150
+ # @return [Array<Annotable::Annotation>] The annotations for the current method declaration
151
+ #
152
+ def current_annotations
153
+ @current_annotations ||= []
154
+ end
155
+
156
+ #
157
+ # Empty the current annotation array.
158
+ #
159
+ # @return [void]
160
+ #
161
+ def reset_current_annotations
162
+ @current_annotations = []
163
+ end
164
+ end
data/sig/annotable.rbs ADDED
@@ -0,0 +1,168 @@
1
+ #
2
+ # Annotable is a module that can extend any class or module to add the ability to annotate its methods.
3
+ #
4
+ # class Needy
5
+ # extend Annotable # Extend your class or module with Annotable
6
+ # annotable :my_annotation, :my_other_annotation # Declare your annotations
7
+ #
8
+ # my_annotation 42, hello: "world!" # Annotate your methods
9
+ # def method_needing_meta_data
10
+ # # ...
11
+ # end
12
+ #
13
+ # def regular_method
14
+ # # ...
15
+ # end
16
+ # end
17
+ #
18
+ # `Annotable` adds several methods to your class or module, providing access to annotations and their metadata:
19
+ #
20
+ # Needy.annotated_method_exist? :method_needing_meta_data # => true
21
+ # Needy.annotated_method_exist? :regular_method # => false
22
+ # Needy.annotated_methods
23
+ # # => [#<Annotable::Method
24
+ # # @annotations=[
25
+ # # #<Annotable::Annotation
26
+ # # @name=:my_annotation,
27
+ # # @options={:hello=>"world!"},
28
+ # # @params=[42]>
29
+ # # ],
30
+ # # @name=:method_needing_meta_data>
31
+ # # ]
32
+ #
33
+ # `Annotable::Method` represents a method name along with its annotations:
34
+ #
35
+ # method = Needy.annotated_methods.first
36
+ # method.name # => :method_needing_meta_data
37
+ # method.annotations
38
+ # # => [
39
+ # # #<Annotable::Annotation
40
+ # # @name=:my_annotation,
41
+ # # @options={:hello=>"world!"},
42
+ # # @params=[42]>
43
+ # # ]
44
+ #
45
+ # `Annotable::Annotation` contains annotation's name and metadata:
46
+ #
47
+ # annotation = method.annotations.first
48
+ # annotation.name # => :my_annotation
49
+ # annotation.params # => [42]
50
+ # annotation.options # => {:hello => "world!"}
51
+ module Annotable
52
+ VERSION: String
53
+
54
+ # Declares annotations usable in the module or class.
55
+ #
56
+ # annotable :my_annotation, :my_other_annotation
57
+ #
58
+ # This will generate two class methods named after the given symbols.
59
+ # These methods, will push a new `Annotation` in the `current_annotation` array.
60
+ #
61
+ # _@param_ `annotation_names` — The names of annotations to declare
62
+ def annotable: (*::Array[Symbol] annotation_names) -> void
63
+
64
+ # Return all annotated methods or those matching the given annotation names.
65
+ #
66
+ # _@param_ `names` — The annotation names to find
67
+ #
68
+ # _@return_ — The annotated methods
69
+ def annotated_methods: (*::Array[Symbol] names) -> ::Array[Annotable::Method]
70
+
71
+ # Check if an annotated method exists based in its name.
72
+ #
73
+ # _@param_ `name` — The name to check
74
+ #
75
+ # _@return_ — True if the annotated method exists, false otherwise
76
+ def annotated_method_exist?: (Symbol name) -> bool
77
+
78
+ # A callback called by Ruby when a method is added into the class or module.
79
+ #
80
+ # _@param_ `name` — The name of the created method
81
+ def method_added: (Symbol name) -> void
82
+
83
+ # Remove an annotated method based on its name.
84
+ #
85
+ # _@param_ `name` — The name of the method to delete
86
+ def remove_annotated_method: (Symbol name) -> void
87
+
88
+ # Annotation found for the current method declaration.
89
+ #
90
+ # _@return_ — The annotations for the current method declaration
91
+ def current_annotations: () -> ::Array[Annotable::Annotation]
92
+
93
+ # Empty the current annotation array.
94
+ def reset_current_annotations: () -> void
95
+
96
+ #
97
+ # An annotated method with its annotations.
98
+ #
99
+ # one = Annotation.new(:one)
100
+ # two = Annotation.new(:two)
101
+ # some_method = Method.new(:some_method, one, two)
102
+ #
103
+ # some_method.annotation_exist?(:one) # => true
104
+ # some_method.annotation_exist?(:no) # => false
105
+ # some_method.select_annotations(:one) # => [#<Annotable::Annotation @name=:one, @options={}, @params=[]>]
106
+ class Method
107
+ # Creates a new annotated method.
108
+ #
109
+ # _@param_ `name` — The method name
110
+ #
111
+ # _@param_ `annotations` — The annotations linked to this method
112
+ def initialize: (Symbol name, *::Array[Annotation] annotations) -> void
113
+
114
+ # Determines whether annotation exists based on its name.
115
+ #
116
+ # _@param_ `name` — The annotation's name to check for
117
+ #
118
+ # _@return_ — True if the annotation exists, false otherwise
119
+ def annotation_exist?: (Symbol name) -> bool
120
+
121
+ # Returns all annotations matching the given names.
122
+ #
123
+ # _@param_ `names` — Names of the annotations to select
124
+ #
125
+ # _@return_ — The matching annotations
126
+ def select_annotations: (*::Array[Symbol] names) -> ::Array[Annotation]
127
+
128
+ # Finds the first annotation matching one of the given names.
129
+ #
130
+ # _@param_ `*names` — The annotation names to find
131
+ #
132
+ # _@return_ — The matching annotation
133
+ def find_annotation: (*::Array[Symbol] names) -> Annotation
134
+
135
+ # _@return_ — The method name
136
+ attr_reader name: Symbol
137
+
138
+ # _@return_ — The annotations declared for this method
139
+ attr_reader annotations: ::Array[Annotation]
140
+ end
141
+
142
+ #
143
+ # Encapsulates annotation data: name, params & options.
144
+ #
145
+ # my_annotation = Annotation.new(:name, ["some", "params"], {some: "options"})
146
+ # my_annotation.name # => :name
147
+ # my_annotation.params # => ["some", "params"]
148
+ # my_annotation.options # => {some: "options"}
149
+ class Annotation
150
+ # Creates an new annotation.
151
+ #
152
+ # _@param_ `name` — The annotation's name
153
+ #
154
+ # _@param_ `params` — The annotation's params
155
+ #
156
+ # _@param_ `options` — The annotation's options
157
+ def initialize: (Symbol name, ?::Array[Object] params, ?::Hash[Symbol, Object] options) -> void
158
+
159
+ # _@return_ — The annotation's name
160
+ attr_reader name: Symbol
161
+
162
+ # _@return_ — The annotation's params
163
+ attr_reader params: ::Array[Object]
164
+
165
+ # _@return_ — The annotation's options
166
+ attr_reader options: ::Hash[Symbol, Object]
167
+ end
168
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: annotable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Mathieu MOREL
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-03-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '13.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '13.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.29'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.29'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.21'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.21'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sord
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '4.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '4.0'
97
+ description: Provides a simple way to add annotations to your method declarations.
98
+ email:
99
+ - mathieu@lamanufacture.dev
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".rspec"
105
+ - ".rubocop.yml"
106
+ - ".vscode/settings.json"
107
+ - COPYING
108
+ - COPYING.LESSER
109
+ - Gemfile
110
+ - Gemfile.lock
111
+ - README.md
112
+ - Rakefile
113
+ - annotable.gemspec
114
+ - lib/annotable.rb
115
+ - lib/annotable/annotation.rb
116
+ - lib/annotable/method.rb
117
+ - lib/annotable/version.rb
118
+ - sig/annotable.rbs
119
+ homepage: https://github.com/ductr-io/annotable
120
+ licenses:
121
+ - LGPL-3.0-or-later
122
+ metadata:
123
+ allowed_push_host: https://rubygems.org
124
+ rubygems_mfa_required: 'true'
125
+ homepage_uri: https://github.com/ductr-io/annotable
126
+ source_code_uri: https://github.com/ductr-io/annotable
127
+ changelog_uri: https://github.com/ductr-io/annotable/releases
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: 3.1.0
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubygems_version: 3.3.26
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: A simple zero-dependency method annotation gem
147
+ test_files: []