unparser 0.4.7 → 0.6.7
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/README.md +40 -9
- data/bin/unparser +2 -2
- data/lib/unparser/abstract_type.rb +121 -0
- data/lib/unparser/adamantium/method_builder.rb +111 -0
- data/lib/unparser/adamantium.rb +150 -0
- data/lib/unparser/anima/attribute.rb +59 -0
- data/lib/unparser/anima/error.rb +23 -0
- data/lib/unparser/anima.rb +184 -0
- data/lib/unparser/ast/local_variable_scope.rb +6 -76
- data/lib/unparser/ast.rb +1 -3
- data/lib/unparser/buffer.rb +14 -25
- data/lib/unparser/cli.rb +85 -77
- data/lib/unparser/{cli/color.rb → color.rb} +4 -14
- data/lib/unparser/comments.rb +0 -26
- data/lib/unparser/concord.rb +114 -0
- data/lib/unparser/constants.rb +4 -53
- data/lib/unparser/diff.rb +98 -0
- data/lib/unparser/dsl.rb +0 -32
- data/lib/unparser/either.rb +153 -0
- data/lib/unparser/emitter/alias.rb +2 -8
- data/lib/unparser/emitter/args.rb +45 -0
- data/lib/unparser/emitter/argument.rb +13 -169
- data/lib/unparser/emitter/array.rb +27 -0
- data/lib/unparser/emitter/array_pattern.rb +29 -0
- data/lib/unparser/emitter/assignment.rb +36 -127
- data/lib/unparser/emitter/begin.rb +9 -84
- data/lib/unparser/emitter/binary.rb +7 -20
- data/lib/unparser/emitter/block.rb +57 -41
- data/lib/unparser/emitter/case.rb +6 -48
- data/lib/unparser/emitter/case_guard.rb +27 -0
- data/lib/unparser/emitter/case_match.rb +40 -0
- data/lib/unparser/emitter/cbase.rb +1 -3
- data/lib/unparser/emitter/class.rb +6 -26
- data/lib/unparser/emitter/const_pattern.rb +24 -0
- data/lib/unparser/emitter/def.rb +7 -51
- data/lib/unparser/emitter/defined.rb +2 -12
- data/lib/unparser/emitter/dstr.rb +22 -0
- data/lib/unparser/emitter/dsym.rb +41 -0
- data/lib/unparser/emitter/find_pattern.rb +18 -0
- data/lib/unparser/emitter/flipflop.rb +11 -10
- data/lib/unparser/emitter/float.rb +29 -0
- data/lib/unparser/emitter/flow_modifier.rb +15 -53
- data/lib/unparser/emitter/for.rb +5 -19
- data/lib/unparser/emitter/hash.rb +36 -0
- data/lib/unparser/emitter/hash_pattern.rb +67 -0
- data/lib/unparser/emitter/hookexe.rb +5 -11
- data/lib/unparser/emitter/if.rb +15 -71
- data/lib/unparser/emitter/in_match.rb +21 -0
- data/lib/unparser/emitter/in_pattern.rb +36 -0
- data/lib/unparser/emitter/index.rb +22 -89
- data/lib/unparser/emitter/kwargs.rb +13 -0
- data/lib/unparser/emitter/kwbegin.rb +31 -0
- data/lib/unparser/emitter/lambda.rb +0 -8
- data/lib/unparser/emitter/masgn.rb +20 -0
- data/lib/unparser/emitter/match.rb +3 -17
- data/lib/unparser/emitter/match_alt.rb +23 -0
- data/lib/unparser/emitter/match_as.rb +21 -0
- data/lib/unparser/emitter/match_pattern.rb +30 -0
- data/lib/unparser/emitter/match_pattern_p.rb +20 -0
- data/lib/unparser/emitter/match_rest.rb +33 -0
- data/lib/unparser/emitter/match_var.rb +19 -0
- data/lib/unparser/emitter/mlhs.rb +40 -0
- data/lib/unparser/emitter/module.rb +3 -9
- data/lib/unparser/emitter/op_assign.rb +14 -29
- data/lib/unparser/emitter/pair.rb +33 -0
- data/lib/unparser/emitter/pin.rb +19 -0
- data/lib/unparser/emitter/primitive.rb +93 -0
- data/lib/unparser/emitter/range.rb +35 -0
- data/lib/unparser/emitter/regexp.rb +35 -0
- data/lib/unparser/emitter/repetition.rb +17 -57
- data/lib/unparser/emitter/rescue.rb +1 -97
- data/lib/unparser/emitter/root.rb +17 -1
- data/lib/unparser/emitter/send.rb +10 -219
- data/lib/unparser/emitter/simple.rb +33 -0
- data/lib/unparser/emitter/splat.rb +13 -19
- data/lib/unparser/emitter/super.rb +1 -29
- data/lib/unparser/emitter/undef.rb +1 -9
- data/lib/unparser/emitter/variable.rb +1 -31
- data/lib/unparser/emitter/xstr.rb +72 -0
- data/lib/unparser/emitter/yield.rb +1 -9
- data/lib/unparser/emitter.rb +24 -425
- data/lib/unparser/equalizer.rb +98 -0
- data/lib/unparser/generation.rb +252 -0
- data/lib/unparser/node_details/send.rb +65 -0
- data/lib/unparser/node_details.rb +21 -0
- data/lib/unparser/node_helpers.rb +48 -6
- data/lib/unparser/validation.rb +172 -0
- data/lib/unparser/writer/binary.rb +99 -0
- data/lib/unparser/writer/dynamic_string.rb +211 -0
- data/lib/unparser/writer/resbody.rb +40 -0
- data/lib/unparser/writer/rescue.rb +43 -0
- data/lib/unparser/{emitter → writer}/send/attribute_assignment.rb +11 -26
- data/lib/unparser/writer/send/binary.rb +27 -0
- data/lib/unparser/writer/send/conditional.rb +25 -0
- data/lib/unparser/writer/send/regular.rb +33 -0
- data/lib/unparser/{emitter → writer}/send/unary.rb +10 -17
- data/lib/unparser/writer/send.rb +115 -0
- data/lib/unparser/writer.rb +15 -0
- data/lib/unparser.rb +161 -77
- metadata +100 -157
- data/.circleci/config.yml +0 -49
- data/.gitignore +0 -37
- data/.rspec +0 -4
- data/.rubocop.yml +0 -9
- data/Changelog.md +0 -156
- data/Gemfile +0 -9
- data/Gemfile.lock +0 -181
- data/LICENSE +0 -20
- data/Rakefile +0 -22
- data/config/devtools.yml +0 -2
- data/config/flay.yml +0 -3
- data/config/flog.yml +0 -2
- data/config/mutant.yml +0 -6
- data/config/reek.yml +0 -98
- data/config/rubocop.yml +0 -122
- data/config/yardstick.yml +0 -2
- data/lib/unparser/cli/differ.rb +0 -152
- data/lib/unparser/cli/source.rb +0 -267
- data/lib/unparser/emitter/empty.rb +0 -23
- data/lib/unparser/emitter/ensure.rb +0 -37
- data/lib/unparser/emitter/literal/array.rb +0 -29
- data/lib/unparser/emitter/literal/dynamic.rb +0 -53
- data/lib/unparser/emitter/literal/dynamic_body.rb +0 -132
- data/lib/unparser/emitter/literal/execute_string.rb +0 -38
- data/lib/unparser/emitter/literal/hash.rb +0 -156
- data/lib/unparser/emitter/literal/primitive.rb +0 -145
- data/lib/unparser/emitter/literal/range.rb +0 -36
- data/lib/unparser/emitter/literal/regexp.rb +0 -114
- data/lib/unparser/emitter/literal/singleton.rb +0 -26
- data/lib/unparser/emitter/literal.rb +0 -10
- data/lib/unparser/emitter/meta.rb +0 -16
- data/lib/unparser/emitter/redo.rb +0 -25
- data/lib/unparser/emitter/resbody.rb +0 -76
- data/lib/unparser/emitter/retry.rb +0 -25
- data/lib/unparser/emitter/send/binary.rb +0 -57
- data/lib/unparser/emitter/send/conditional.rb +0 -40
- data/lib/unparser/emitter/send/regular.rb +0 -40
- data/lib/unparser/preprocessor.rb +0 -159
- data/spec/integration/unparser/corpus_spec.rb +0 -111
- data/spec/integrations.yml +0 -92
- data/spec/spec_helper.rb +0 -20
- data/spec/unit/unparser/buffer/append_spec.rb +0 -24
- data/spec/unit/unparser/buffer/append_without_prefix_spec.rb +0 -23
- data/spec/unit/unparser/buffer/capture_content_spec.rb +0 -17
- data/spec/unit/unparser/buffer/content_spec.rb +0 -38
- data/spec/unit/unparser/buffer/fresh_line_spec.rb +0 -20
- data/spec/unit/unparser/buffer/indent_spec.rb +0 -20
- data/spec/unit/unparser/buffer/nl_spec.rb +0 -16
- data/spec/unit/unparser/buffer/unindent_spec.rb +0 -20
- data/spec/unit/unparser/comments/consume_spec.rb +0 -22
- data/spec/unit/unparser/comments/take_all_spec.rb +0 -19
- data/spec/unit/unparser/comments/take_before_spec.rb +0 -46
- data/spec/unit/unparser/comments/take_eol_comments_spec.rb +0 -32
- data/spec/unit/unparser/emitter/class_methods/handle_spec.rb +0 -17
- data/spec/unit/unparser_spec.rb +0 -1849
- data/unparser.gemspec +0 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43c55e0072230fba02c24266555e1599559396739677e4371b6c9bca4b2a29b1
|
|
4
|
+
data.tar.gz: 4743b675c63ed3cf84ee3c784b70fd5078f8ed4e022e02bde27473a7bf44dbbe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94bc33d333f3652cbdfa12169432d7a139b568cb72d4ae08994921760d2f7c8348a8d35f717053c059866ce7e640d08c9af6495a5e37132b2b667d1895b889da
|
|
7
|
+
data.tar.gz: ee2ee8e37250de111c419566c74d7b5954b02137f9e6fcb88cf9ff26820583698faf2533e6d01ae5738481bab91797060e2773781e9c93cb8da26efce602c1b0
|
data/README.md
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
unparser
|
|
2
2
|
========
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
[](https://codeclimate.com/github/mbj/unparser)
|
|
4
|
+

|
|
6
5
|
[](https://rubygems.org/gems/unparser)
|
|
7
6
|
|
|
8
|
-
Generate equivalent source for ASTs from
|
|
7
|
+
Generate equivalent source for ASTs from [parser](https://github.com/whitequark/parser).
|
|
9
8
|
|
|
10
9
|
The following constraints apply:
|
|
11
10
|
|
|
12
11
|
* No support for macruby extensions
|
|
13
12
|
* Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format
|
|
14
|
-
* Only support for Ruby >= 2.
|
|
13
|
+
* Only support for Ruby >= 2.7
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
Notable Users:
|
|
16
|
+
|
|
17
|
+
* [mutant](https://github.com/mbj/mutant) - Code review engine via mutation testing.
|
|
18
|
+
* [ruby-next](https://github.com/ruby-next/ruby-next) - Ruby Syntax Backports.
|
|
19
|
+
* May other [reverse-dependencies](https://rubygems.org/gems/unparser/reverse_dependencies).
|
|
20
|
+
|
|
21
|
+
(if you want your tool to be mentioned here please PR the addition with a TLDR of your use case).
|
|
18
22
|
|
|
19
23
|
Public API:
|
|
20
24
|
-----------
|
|
@@ -87,16 +91,26 @@ RUBY
|
|
|
87
91
|
|
|
88
92
|
generated = Unparser.unparse(node) # ["foo", "bar"], NOT %w[foo bar] !
|
|
89
93
|
|
|
90
|
-
code == generated
|
|
94
|
+
code == generated # false, not identical code
|
|
91
95
|
Unparser.parse(generated) == node # true, but identical AST
|
|
92
96
|
```
|
|
93
97
|
|
|
94
98
|
Summary: unparser does not reproduce your source! It produces equivalent source.
|
|
95
99
|
|
|
100
|
+
Ruby Versions:
|
|
101
|
+
--------------
|
|
102
|
+
|
|
103
|
+
Unparsers primay reason for existance is mutant and its
|
|
104
|
+
supported [Ruby-Versions](https://github.com/mbj/mutant#ruby-versions).
|
|
105
|
+
|
|
106
|
+
Basically: All non EOL MRI releases.
|
|
107
|
+
|
|
108
|
+
If you need to generate Ruby Syntax outside of this band feel free to contact me (email in gemspec).
|
|
109
|
+
|
|
96
110
|
Testing:
|
|
97
111
|
--------
|
|
98
112
|
|
|
99
|
-
Unparser currently successfully round trips almost all ruby code around. Using
|
|
113
|
+
Unparser currently successfully round trips almost all ruby code around. Using Ruby >= 2.6.
|
|
100
114
|
If there is a non round trippable example that is NOT subjected to known [Limitations](#limitations).
|
|
101
115
|
please report a bug.
|
|
102
116
|
|
|
@@ -154,6 +168,18 @@ People
|
|
|
154
168
|
|
|
155
169
|
Various people contributed to this repository. See [Contributors](https://github.com/mbj/unparser/graphs/contributors).
|
|
156
170
|
|
|
171
|
+
Included Libraries
|
|
172
|
+
------------------
|
|
173
|
+
|
|
174
|
+
For dependency reduction reasons unparser ships vendored (and reduced) versions of:
|
|
175
|
+
|
|
176
|
+
* [abstract_type](https://github.com/mbj/concord) -> Unparser::AbstractType
|
|
177
|
+
* [adamantium](https://github.com/dkubb/adamantium) -> Unparser::Adamantium
|
|
178
|
+
* [anima](https://github.com/mbj/concord) -> Unparser::Anima
|
|
179
|
+
* [concord](https://github.com/mbj/concord) -> Unparser::Concord
|
|
180
|
+
* [memoizable](https://github.com/dkubb/memoizable) -> Unparser::Adamantium
|
|
181
|
+
* [mprelude](https://github.com/dkubb/memoizable) -> Unparser::Either
|
|
182
|
+
|
|
157
183
|
Contributing
|
|
158
184
|
-------------
|
|
159
185
|
|
|
@@ -161,10 +187,15 @@ Contributing
|
|
|
161
187
|
* Make your feature addition or bug fix.
|
|
162
188
|
* Add tests for it. This is important so I don't break it in a
|
|
163
189
|
future version unintentionally.
|
|
164
|
-
* Commit, do not mess with
|
|
190
|
+
* Commit, do not mess with version
|
|
165
191
|
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
|
166
192
|
* Send me a pull request. Bonus points for topic branches.
|
|
167
193
|
|
|
194
|
+
Known Users
|
|
195
|
+
-------------
|
|
196
|
+
|
|
197
|
+
* [RailsRocket](https://www.railsrocket.app) - A no-code app builder that creates Rails apps
|
|
198
|
+
|
|
168
199
|
License
|
|
169
200
|
-------
|
|
170
201
|
|
data/bin/unparser
CHANGED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unparser
|
|
4
|
+
# Module to allow class and methods to be abstract
|
|
5
|
+
#
|
|
6
|
+
# Original code before vendoring and reduction from: https://github.com/dkubb/abstract_type.
|
|
7
|
+
module AbstractType
|
|
8
|
+
|
|
9
|
+
# Hook called when module is included
|
|
10
|
+
#
|
|
11
|
+
# @param [Module] descendant
|
|
12
|
+
# the module or class including AbstractType
|
|
13
|
+
#
|
|
14
|
+
# @return [undefined]
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
def self.included(descendant)
|
|
18
|
+
super
|
|
19
|
+
create_new_method(descendant)
|
|
20
|
+
descendant.extend(AbstractMethodDeclarations)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private_class_method :included
|
|
24
|
+
|
|
25
|
+
# Define the new method on the abstract type
|
|
26
|
+
#
|
|
27
|
+
# Ensures that the instance cannot be of the abstract type
|
|
28
|
+
# and must be a descendant.
|
|
29
|
+
#
|
|
30
|
+
# @param [Class] abstract_class
|
|
31
|
+
#
|
|
32
|
+
# @return [undefined]
|
|
33
|
+
#
|
|
34
|
+
# @api private
|
|
35
|
+
def self.create_new_method(abstract_class)
|
|
36
|
+
abstract_class.define_singleton_method(:new) do |*args, &block|
|
|
37
|
+
if equal?(abstract_class)
|
|
38
|
+
fail NotImplementedError, "#{self} is an abstract type"
|
|
39
|
+
else
|
|
40
|
+
super(*args, &block)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private_class_method :create_new_method
|
|
46
|
+
|
|
47
|
+
module AbstractMethodDeclarations
|
|
48
|
+
|
|
49
|
+
# Create abstract instance methods
|
|
50
|
+
#
|
|
51
|
+
# @example
|
|
52
|
+
# class Foo
|
|
53
|
+
# include AbstractType
|
|
54
|
+
#
|
|
55
|
+
# # Create an abstract instance method
|
|
56
|
+
# abstract_method :some_method
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# @param [Array<#to_s>] names
|
|
60
|
+
#
|
|
61
|
+
# @return [self]
|
|
62
|
+
#
|
|
63
|
+
# @api public
|
|
64
|
+
def abstract_method(*names)
|
|
65
|
+
names.each(&method(:create_abstract_instance_method))
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Create abstract singleton methods
|
|
70
|
+
#
|
|
71
|
+
# @example
|
|
72
|
+
# class Foo
|
|
73
|
+
# include AbstractType
|
|
74
|
+
#
|
|
75
|
+
# # Create an abstract instance method
|
|
76
|
+
# abstract_singleton_method :some_method
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# @param [Array<#to_s>] names
|
|
80
|
+
#
|
|
81
|
+
# @return [self]
|
|
82
|
+
#
|
|
83
|
+
# @api private
|
|
84
|
+
def abstract_singleton_method(*names)
|
|
85
|
+
names.each(&method(:create_abstract_singleton_method))
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Create abstract singleton method
|
|
92
|
+
#
|
|
93
|
+
# @param [#to_s] name
|
|
94
|
+
# the name of the method to create
|
|
95
|
+
#
|
|
96
|
+
# @return [undefined]
|
|
97
|
+
#
|
|
98
|
+
# @api private
|
|
99
|
+
def create_abstract_singleton_method(name)
|
|
100
|
+
define_singleton_method(name) do |*|
|
|
101
|
+
fail NotImplementedError, "#{self}.#{name} is not implemented"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Create abstract instance method
|
|
106
|
+
#
|
|
107
|
+
# @param [#to_s] name
|
|
108
|
+
# the name of the method to create
|
|
109
|
+
#
|
|
110
|
+
# @return [undefined]
|
|
111
|
+
#
|
|
112
|
+
# @api private
|
|
113
|
+
def create_abstract_instance_method(name)
|
|
114
|
+
define_method(name) do |*|
|
|
115
|
+
fail NotImplementedError, "#{self.class}##{name} is not implemented"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end # AbstractMethodDeclarations
|
|
120
|
+
end # AbstractType
|
|
121
|
+
end # Unparser
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unparser
|
|
4
|
+
module Adamantium
|
|
5
|
+
# Build the memoized method
|
|
6
|
+
class MethodBuilder
|
|
7
|
+
|
|
8
|
+
# Raised when the method arity is invalid
|
|
9
|
+
class InvalidArityError < ArgumentError
|
|
10
|
+
|
|
11
|
+
# Initialize an invalid arity exception
|
|
12
|
+
#
|
|
13
|
+
# @param [Module] descendant
|
|
14
|
+
# @param [Symbol] method
|
|
15
|
+
# @param [Integer] arity
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
def initialize(descendant, method, arity)
|
|
19
|
+
super("Cannot memoize #{descendant}##{method}, its arity is #{arity}")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end # InvalidArityError
|
|
23
|
+
|
|
24
|
+
# Raised when a block is passed to a memoized method
|
|
25
|
+
class BlockNotAllowedError < ArgumentError
|
|
26
|
+
|
|
27
|
+
# Initialize a block not allowed exception
|
|
28
|
+
#
|
|
29
|
+
# @param [Module] descendant
|
|
30
|
+
# @param [Symbol] method
|
|
31
|
+
#
|
|
32
|
+
# @api private
|
|
33
|
+
def initialize(descendant, method)
|
|
34
|
+
super("Cannot pass a block to #{descendant}##{method}, it is memoized")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end # BlockNotAllowedError
|
|
38
|
+
|
|
39
|
+
# Initialize an object to build a memoized method
|
|
40
|
+
#
|
|
41
|
+
# @param [Module] descendant
|
|
42
|
+
# @param [Symbol] method_name
|
|
43
|
+
#
|
|
44
|
+
# @return [undefined]
|
|
45
|
+
#
|
|
46
|
+
# @api private
|
|
47
|
+
def initialize(descendant, method_name)
|
|
48
|
+
@descendant = descendant
|
|
49
|
+
@method_name = method_name
|
|
50
|
+
@original_visibility = visibility
|
|
51
|
+
@original_method = @descendant.instance_method(@method_name)
|
|
52
|
+
assert_arity(@original_method.arity)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Build a new memoized method
|
|
56
|
+
#
|
|
57
|
+
# @example
|
|
58
|
+
# method_builder.call # => creates new method
|
|
59
|
+
#
|
|
60
|
+
# @return [UnboundMethod]
|
|
61
|
+
#
|
|
62
|
+
# @api public
|
|
63
|
+
def call
|
|
64
|
+
remove_original_method
|
|
65
|
+
create_memoized_method
|
|
66
|
+
set_method_visibility
|
|
67
|
+
@original_method
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def assert_arity(arity)
|
|
73
|
+
if arity.nonzero?
|
|
74
|
+
fail InvalidArityError.new(@descendant, @method_name, arity)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def remove_original_method
|
|
79
|
+
name = @method_name
|
|
80
|
+
@descendant.module_eval { undef_method(name) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def create_memoized_method
|
|
84
|
+
name = @method_name
|
|
85
|
+
method = @original_method
|
|
86
|
+
@descendant.module_eval do
|
|
87
|
+
define_method(name) do |&block|
|
|
88
|
+
fail BlockNotAllowedError.new(self.class, name) if block
|
|
89
|
+
|
|
90
|
+
memoized_method_cache.fetch(name) do
|
|
91
|
+
method.bind(self).call.freeze
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def set_method_visibility
|
|
98
|
+
@descendant.__send__(@original_visibility, @method_name)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def visibility
|
|
102
|
+
if @descendant.private_method_defined?(@method_name) then :private
|
|
103
|
+
elsif @descendant.protected_method_defined?(@method_name) then :protected
|
|
104
|
+
else
|
|
105
|
+
:public
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end # MethodBuilder
|
|
110
|
+
end # Adamantium
|
|
111
|
+
end # Unparser
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unparser
|
|
4
|
+
# Allows objects to be made immutable
|
|
5
|
+
#
|
|
6
|
+
# Original code before vendoring and reduction from: https://github.com/dkubb/adamantium.
|
|
7
|
+
module Adamantium
|
|
8
|
+
module InstanceMethods
|
|
9
|
+
# A noop #dup for immutable objects
|
|
10
|
+
#
|
|
11
|
+
# @return [self]
|
|
12
|
+
#
|
|
13
|
+
# @api public
|
|
14
|
+
def dup
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Freeze the object
|
|
19
|
+
#
|
|
20
|
+
# @return [Object]
|
|
21
|
+
#
|
|
22
|
+
# @api public
|
|
23
|
+
def freeze
|
|
24
|
+
memoized_method_cache
|
|
25
|
+
super()
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def memoized_method_cache
|
|
31
|
+
@memoized_method_cache ||= Memory.new({})
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end # InstanceMethods
|
|
35
|
+
|
|
36
|
+
# Storage for memoized methods
|
|
37
|
+
class Memory
|
|
38
|
+
|
|
39
|
+
# Initialize the memory storage for memoized methods
|
|
40
|
+
#
|
|
41
|
+
# @return [undefined]
|
|
42
|
+
#
|
|
43
|
+
# @api private
|
|
44
|
+
def initialize(values)
|
|
45
|
+
@values = values
|
|
46
|
+
@monitor = Monitor.new
|
|
47
|
+
freeze
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Fetch the value from memory, or evaluate if it does not exist
|
|
51
|
+
#
|
|
52
|
+
# @param [Symbol] name
|
|
53
|
+
#
|
|
54
|
+
# @yieldreturn [Object]
|
|
55
|
+
# the value to memoize
|
|
56
|
+
#
|
|
57
|
+
# @api public
|
|
58
|
+
def fetch(name)
|
|
59
|
+
@values.fetch(name) do # check for the key
|
|
60
|
+
@monitor.synchronize do # acquire a lock if the key is not found
|
|
61
|
+
@values.fetch(name) do # recheck under lock
|
|
62
|
+
@values[name] = yield # set the value
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end # Memory
|
|
68
|
+
|
|
69
|
+
# Methods mixed in to adamantium classes
|
|
70
|
+
module ClassMethods
|
|
71
|
+
|
|
72
|
+
# Instantiate a new frozen object
|
|
73
|
+
#
|
|
74
|
+
# @return [Object]
|
|
75
|
+
#
|
|
76
|
+
# @api public
|
|
77
|
+
def new(*)
|
|
78
|
+
super.freeze
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end # ClassMethods
|
|
82
|
+
|
|
83
|
+
# Methods mixed in to adamantium modules
|
|
84
|
+
module ModuleMethods
|
|
85
|
+
|
|
86
|
+
# Memoize a list of methods
|
|
87
|
+
#
|
|
88
|
+
# @param [Array<#to_s>] methods
|
|
89
|
+
# a list of methods to memoize
|
|
90
|
+
#
|
|
91
|
+
# @return [self]
|
|
92
|
+
#
|
|
93
|
+
# @api public
|
|
94
|
+
def memoize(*methods)
|
|
95
|
+
methods.each(&method(:memoize_method))
|
|
96
|
+
self
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Test if method is memoized
|
|
100
|
+
#
|
|
101
|
+
# @param [Symbol] name
|
|
102
|
+
#
|
|
103
|
+
# @return [Bool]
|
|
104
|
+
def memoized?(method_name)
|
|
105
|
+
memoized_methods.key?(method_name)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Return unmemoized instance method
|
|
109
|
+
#
|
|
110
|
+
# @param [Symbol] name
|
|
111
|
+
#
|
|
112
|
+
# @return [UnboundMethod]
|
|
113
|
+
# the memoized method
|
|
114
|
+
#
|
|
115
|
+
# @raise [NameError]
|
|
116
|
+
# raised if the method is unknown
|
|
117
|
+
#
|
|
118
|
+
# @api public
|
|
119
|
+
def unmemoized_instance_method(method_name)
|
|
120
|
+
memoized_methods.fetch(method_name) do
|
|
121
|
+
fail ArgumentError, "##{method_name} is not memoized"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
def memoize_method(method_name)
|
|
128
|
+
if memoized_methods.key?(method_name)
|
|
129
|
+
fail ArgumentError, "##{method_name} is already memoized"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
memoized_methods[method_name] = MethodBuilder.new(self, method_name).call
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def memoized_methods
|
|
136
|
+
@memoized_methods ||= {}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
end # ModuleMethods
|
|
140
|
+
|
|
141
|
+
def self.included(descendant)
|
|
142
|
+
descendant.class_eval do
|
|
143
|
+
include InstanceMethods
|
|
144
|
+
extend ModuleMethods
|
|
145
|
+
extend ClassMethods if instance_of?(Class)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
private_class_method :included
|
|
149
|
+
end # Adamantium
|
|
150
|
+
end # Unparser
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unparser
|
|
4
|
+
class Anima
|
|
5
|
+
# An attribute
|
|
6
|
+
class Attribute
|
|
7
|
+
include Adamantium, Equalizer.new(:name)
|
|
8
|
+
|
|
9
|
+
# Initialize attribute
|
|
10
|
+
#
|
|
11
|
+
# @param [Symbol] name
|
|
12
|
+
def initialize(name)
|
|
13
|
+
@name = name
|
|
14
|
+
@instance_variable_name = :"@#{name}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Return attribute name
|
|
18
|
+
#
|
|
19
|
+
# @return [Symbol]
|
|
20
|
+
attr_reader :name
|
|
21
|
+
|
|
22
|
+
# Return instance variable name
|
|
23
|
+
#
|
|
24
|
+
# @return [Symbol]
|
|
25
|
+
attr_reader :instance_variable_name
|
|
26
|
+
|
|
27
|
+
# Load attribute
|
|
28
|
+
#
|
|
29
|
+
# @param [Object] object
|
|
30
|
+
# @param [Hash] attributes
|
|
31
|
+
#
|
|
32
|
+
# @return [self]
|
|
33
|
+
def load(object, attributes)
|
|
34
|
+
set(object, attributes.fetch(name))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Get attribute value from object
|
|
38
|
+
#
|
|
39
|
+
# @param [Object] object
|
|
40
|
+
#
|
|
41
|
+
# @return [Object]
|
|
42
|
+
def get(object)
|
|
43
|
+
object.public_send(name)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Set attribute value in object
|
|
47
|
+
#
|
|
48
|
+
# @param [Object] object
|
|
49
|
+
# @param [Object] value
|
|
50
|
+
#
|
|
51
|
+
# @return [self]
|
|
52
|
+
def set(object, value)
|
|
53
|
+
object.instance_variable_set(instance_variable_name, value)
|
|
54
|
+
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
end # Attribute
|
|
58
|
+
end # Anima
|
|
59
|
+
end # Unparser
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unparser
|
|
4
|
+
class Anima
|
|
5
|
+
# Abstract base class for anima errors
|
|
6
|
+
class Error < RuntimeError
|
|
7
|
+
FORMAT = '%s attributes missing: %s, unknown: %s'.freeze
|
|
8
|
+
private_constant(*constants(false))
|
|
9
|
+
|
|
10
|
+
# Initialize object
|
|
11
|
+
#
|
|
12
|
+
# @param [Class] klass
|
|
13
|
+
# the class being initialized
|
|
14
|
+
# @param [Enumerable<Symbol>] missing
|
|
15
|
+
# @param [Enumerable<Symbol>] unknown
|
|
16
|
+
#
|
|
17
|
+
# @return [undefined]
|
|
18
|
+
def initialize(klass, missing, unknown)
|
|
19
|
+
super(format(FORMAT, klass, missing, unknown))
|
|
20
|
+
end
|
|
21
|
+
end # Error
|
|
22
|
+
end # Anima
|
|
23
|
+
end # Unparser
|