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.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -9
  3. data/bin/unparser +2 -2
  4. data/lib/unparser/abstract_type.rb +121 -0
  5. data/lib/unparser/adamantium/method_builder.rb +111 -0
  6. data/lib/unparser/adamantium.rb +150 -0
  7. data/lib/unparser/anima/attribute.rb +59 -0
  8. data/lib/unparser/anima/error.rb +23 -0
  9. data/lib/unparser/anima.rb +184 -0
  10. data/lib/unparser/ast/local_variable_scope.rb +6 -76
  11. data/lib/unparser/ast.rb +1 -3
  12. data/lib/unparser/buffer.rb +14 -25
  13. data/lib/unparser/cli.rb +85 -77
  14. data/lib/unparser/{cli/color.rb → color.rb} +4 -14
  15. data/lib/unparser/comments.rb +0 -26
  16. data/lib/unparser/concord.rb +114 -0
  17. data/lib/unparser/constants.rb +4 -53
  18. data/lib/unparser/diff.rb +98 -0
  19. data/lib/unparser/dsl.rb +0 -32
  20. data/lib/unparser/either.rb +153 -0
  21. data/lib/unparser/emitter/alias.rb +2 -8
  22. data/lib/unparser/emitter/args.rb +45 -0
  23. data/lib/unparser/emitter/argument.rb +13 -169
  24. data/lib/unparser/emitter/array.rb +27 -0
  25. data/lib/unparser/emitter/array_pattern.rb +29 -0
  26. data/lib/unparser/emitter/assignment.rb +36 -127
  27. data/lib/unparser/emitter/begin.rb +9 -84
  28. data/lib/unparser/emitter/binary.rb +7 -20
  29. data/lib/unparser/emitter/block.rb +57 -41
  30. data/lib/unparser/emitter/case.rb +6 -48
  31. data/lib/unparser/emitter/case_guard.rb +27 -0
  32. data/lib/unparser/emitter/case_match.rb +40 -0
  33. data/lib/unparser/emitter/cbase.rb +1 -3
  34. data/lib/unparser/emitter/class.rb +6 -26
  35. data/lib/unparser/emitter/const_pattern.rb +24 -0
  36. data/lib/unparser/emitter/def.rb +7 -51
  37. data/lib/unparser/emitter/defined.rb +2 -12
  38. data/lib/unparser/emitter/dstr.rb +22 -0
  39. data/lib/unparser/emitter/dsym.rb +41 -0
  40. data/lib/unparser/emitter/find_pattern.rb +18 -0
  41. data/lib/unparser/emitter/flipflop.rb +11 -10
  42. data/lib/unparser/emitter/float.rb +29 -0
  43. data/lib/unparser/emitter/flow_modifier.rb +15 -53
  44. data/lib/unparser/emitter/for.rb +5 -19
  45. data/lib/unparser/emitter/hash.rb +36 -0
  46. data/lib/unparser/emitter/hash_pattern.rb +67 -0
  47. data/lib/unparser/emitter/hookexe.rb +5 -11
  48. data/lib/unparser/emitter/if.rb +15 -71
  49. data/lib/unparser/emitter/in_match.rb +21 -0
  50. data/lib/unparser/emitter/in_pattern.rb +36 -0
  51. data/lib/unparser/emitter/index.rb +22 -89
  52. data/lib/unparser/emitter/kwargs.rb +13 -0
  53. data/lib/unparser/emitter/kwbegin.rb +31 -0
  54. data/lib/unparser/emitter/lambda.rb +0 -8
  55. data/lib/unparser/emitter/masgn.rb +20 -0
  56. data/lib/unparser/emitter/match.rb +3 -17
  57. data/lib/unparser/emitter/match_alt.rb +23 -0
  58. data/lib/unparser/emitter/match_as.rb +21 -0
  59. data/lib/unparser/emitter/match_pattern.rb +30 -0
  60. data/lib/unparser/emitter/match_pattern_p.rb +20 -0
  61. data/lib/unparser/emitter/match_rest.rb +33 -0
  62. data/lib/unparser/emitter/match_var.rb +19 -0
  63. data/lib/unparser/emitter/mlhs.rb +40 -0
  64. data/lib/unparser/emitter/module.rb +3 -9
  65. data/lib/unparser/emitter/op_assign.rb +14 -29
  66. data/lib/unparser/emitter/pair.rb +33 -0
  67. data/lib/unparser/emitter/pin.rb +19 -0
  68. data/lib/unparser/emitter/primitive.rb +93 -0
  69. data/lib/unparser/emitter/range.rb +35 -0
  70. data/lib/unparser/emitter/regexp.rb +35 -0
  71. data/lib/unparser/emitter/repetition.rb +17 -57
  72. data/lib/unparser/emitter/rescue.rb +1 -97
  73. data/lib/unparser/emitter/root.rb +17 -1
  74. data/lib/unparser/emitter/send.rb +10 -219
  75. data/lib/unparser/emitter/simple.rb +33 -0
  76. data/lib/unparser/emitter/splat.rb +13 -19
  77. data/lib/unparser/emitter/super.rb +1 -29
  78. data/lib/unparser/emitter/undef.rb +1 -9
  79. data/lib/unparser/emitter/variable.rb +1 -31
  80. data/lib/unparser/emitter/xstr.rb +72 -0
  81. data/lib/unparser/emitter/yield.rb +1 -9
  82. data/lib/unparser/emitter.rb +24 -425
  83. data/lib/unparser/equalizer.rb +98 -0
  84. data/lib/unparser/generation.rb +252 -0
  85. data/lib/unparser/node_details/send.rb +65 -0
  86. data/lib/unparser/node_details.rb +21 -0
  87. data/lib/unparser/node_helpers.rb +48 -6
  88. data/lib/unparser/validation.rb +172 -0
  89. data/lib/unparser/writer/binary.rb +99 -0
  90. data/lib/unparser/writer/dynamic_string.rb +211 -0
  91. data/lib/unparser/writer/resbody.rb +40 -0
  92. data/lib/unparser/writer/rescue.rb +43 -0
  93. data/lib/unparser/{emitter → writer}/send/attribute_assignment.rb +11 -26
  94. data/lib/unparser/writer/send/binary.rb +27 -0
  95. data/lib/unparser/writer/send/conditional.rb +25 -0
  96. data/lib/unparser/writer/send/regular.rb +33 -0
  97. data/lib/unparser/{emitter → writer}/send/unary.rb +10 -17
  98. data/lib/unparser/writer/send.rb +115 -0
  99. data/lib/unparser/writer.rb +15 -0
  100. data/lib/unparser.rb +161 -77
  101. metadata +100 -157
  102. data/.circleci/config.yml +0 -49
  103. data/.gitignore +0 -37
  104. data/.rspec +0 -4
  105. data/.rubocop.yml +0 -9
  106. data/Changelog.md +0 -156
  107. data/Gemfile +0 -9
  108. data/Gemfile.lock +0 -181
  109. data/LICENSE +0 -20
  110. data/Rakefile +0 -22
  111. data/config/devtools.yml +0 -2
  112. data/config/flay.yml +0 -3
  113. data/config/flog.yml +0 -2
  114. data/config/mutant.yml +0 -6
  115. data/config/reek.yml +0 -98
  116. data/config/rubocop.yml +0 -122
  117. data/config/yardstick.yml +0 -2
  118. data/lib/unparser/cli/differ.rb +0 -152
  119. data/lib/unparser/cli/source.rb +0 -267
  120. data/lib/unparser/emitter/empty.rb +0 -23
  121. data/lib/unparser/emitter/ensure.rb +0 -37
  122. data/lib/unparser/emitter/literal/array.rb +0 -29
  123. data/lib/unparser/emitter/literal/dynamic.rb +0 -53
  124. data/lib/unparser/emitter/literal/dynamic_body.rb +0 -132
  125. data/lib/unparser/emitter/literal/execute_string.rb +0 -38
  126. data/lib/unparser/emitter/literal/hash.rb +0 -156
  127. data/lib/unparser/emitter/literal/primitive.rb +0 -145
  128. data/lib/unparser/emitter/literal/range.rb +0 -36
  129. data/lib/unparser/emitter/literal/regexp.rb +0 -114
  130. data/lib/unparser/emitter/literal/singleton.rb +0 -26
  131. data/lib/unparser/emitter/literal.rb +0 -10
  132. data/lib/unparser/emitter/meta.rb +0 -16
  133. data/lib/unparser/emitter/redo.rb +0 -25
  134. data/lib/unparser/emitter/resbody.rb +0 -76
  135. data/lib/unparser/emitter/retry.rb +0 -25
  136. data/lib/unparser/emitter/send/binary.rb +0 -57
  137. data/lib/unparser/emitter/send/conditional.rb +0 -40
  138. data/lib/unparser/emitter/send/regular.rb +0 -40
  139. data/lib/unparser/preprocessor.rb +0 -159
  140. data/spec/integration/unparser/corpus_spec.rb +0 -111
  141. data/spec/integrations.yml +0 -92
  142. data/spec/spec_helper.rb +0 -20
  143. data/spec/unit/unparser/buffer/append_spec.rb +0 -24
  144. data/spec/unit/unparser/buffer/append_without_prefix_spec.rb +0 -23
  145. data/spec/unit/unparser/buffer/capture_content_spec.rb +0 -17
  146. data/spec/unit/unparser/buffer/content_spec.rb +0 -38
  147. data/spec/unit/unparser/buffer/fresh_line_spec.rb +0 -20
  148. data/spec/unit/unparser/buffer/indent_spec.rb +0 -20
  149. data/spec/unit/unparser/buffer/nl_spec.rb +0 -16
  150. data/spec/unit/unparser/buffer/unindent_spec.rb +0 -20
  151. data/spec/unit/unparser/comments/consume_spec.rb +0 -22
  152. data/spec/unit/unparser/comments/take_all_spec.rb +0 -19
  153. data/spec/unit/unparser/comments/take_before_spec.rb +0 -46
  154. data/spec/unit/unparser/comments/take_eol_comments_spec.rb +0 -32
  155. data/spec/unit/unparser/emitter/class_methods/handle_spec.rb +0 -17
  156. data/spec/unit/unparser_spec.rb +0 -1849
  157. data/unparser.gemspec +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc149df7cd940bb78d005b8f3d30635a877c270d37f40cfe8e7d9ba8b305ab8a
4
- data.tar.gz: c8a1d3dd4aba58f7fd6a6188341916566857057663d660bd24a2d40f6247158a
3
+ metadata.gz: 43c55e0072230fba02c24266555e1599559396739677e4371b6c9bca4b2a29b1
4
+ data.tar.gz: 4743b675c63ed3cf84ee3c784b70fd5078f8ed4e022e02bde27473a7bf44dbbe
5
5
  SHA512:
6
- metadata.gz: 1cba4e5c9f029441dbca0eb39304478562e76c13023ec22b83dc5eeb58d7640d5c0d83878dad104e45ae6369662f3be0e7141398fb7cf37f6e4932075e305b0b
7
- data.tar.gz: 2c61445c8d43bf01f24bb2f79dd2c49ba96fceef9479c6f1b94a77d7b5eba43fb79f0bbbf7d3995e143b81d208da6ad04852ec761daaf210741f46a049da0893
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
- [![Build Status](https://secure.travis-ci.org/mbj/unparser.svg?branch=master)](http://travis-ci.org/mbj/unparser)
5
- [![Code Climate](https://codeclimate.com/github/mbj/unparser.svg)](https://codeclimate.com/github/mbj/unparser)
4
+ ![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg)
6
5
  [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser)
7
6
 
8
- Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser).
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.5
13
+ * Only support for Ruby >= 2.7
15
14
 
16
- It serves well for [mutant](https://github.com/mbj/mutant) mutators and the in-memory vendoring for self hosting,
17
- and other tooling.
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 # false, not identical code
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 MRI-2.5.x.
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 Rakefile or version
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
@@ -2,9 +2,9 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  trap('INT') do |status|
5
- exit! 128 + status
5
+ exit! status + 128
6
6
  end
7
7
 
8
- require 'unparser/cli'
8
+ require 'unparser'
9
9
 
10
10
  exit Unparser::CLI.run(ARGV)
@@ -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