unparser 0.5.7 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # Original code before vendoring and reduction from: https://github.com/mbj/anima.
5
+ class Anima < Module
6
+ include Adamantium, Equalizer.new(:attributes)
7
+
8
+ # Return names
9
+ #
10
+ # @return [AttributeSet]
11
+ attr_reader :attributes
12
+
13
+ # Initialize object
14
+ #
15
+ # @return [undefined]
16
+ #
17
+ # rubocop:disable Lint/MissingSuper
18
+ def initialize(*names)
19
+ @attributes = names.uniq.map(&Attribute.public_method(:new)).freeze
20
+ end
21
+ # rubocop:enable Lint/MissingSuper
22
+
23
+ # Return new anima with attributes added
24
+ #
25
+ # @return [Anima]
26
+ #
27
+ # @example
28
+ # anima = Anima.new(:foo)
29
+ # anima.add(:bar) # equals Anima.new(:foo, :bar)
30
+ #
31
+ def add(*names)
32
+ new(attribute_names + names)
33
+ end
34
+
35
+ # Return new anima with attributes removed
36
+ #
37
+ # @return [Anima]
38
+ #
39
+ # @example
40
+ # anima = Anima.new(:foo, :bar)
41
+ # anima.remove(:bar) # equals Anima.new(:foo)
42
+ #
43
+ def remove(*names)
44
+ new(attribute_names - names)
45
+ end
46
+
47
+ # Return attributes hash for instance
48
+ #
49
+ # @param [Object] object
50
+ #
51
+ # @return [Hash]
52
+ def attributes_hash(object)
53
+ attributes.each_with_object({}) do |attribute, attributes_hash|
54
+ attributes_hash[attribute.name] = attribute.get(object)
55
+ end
56
+ end
57
+
58
+ # Return attribute names
59
+ #
60
+ # @return [Enumerable<Symbol>]
61
+ def attribute_names
62
+ attributes.map(&:name)
63
+ end
64
+ memoize :attribute_names
65
+
66
+ # Initialize instance
67
+ #
68
+ # @param [Object] object
69
+ #
70
+ # @param [Hash] attribute_hash
71
+ #
72
+ # @return [self]
73
+ def initialize_instance(object, attribute_hash)
74
+ assert_known_attributes(object.class, attribute_hash)
75
+ attributes.each do |attribute|
76
+ attribute.load(object, attribute_hash)
77
+ end
78
+ self
79
+ end
80
+
81
+ # Static instance methods for anima infected classes
82
+ module InstanceMethods
83
+ # Initialize an anima infected object
84
+ #
85
+ # @param [#to_h] attributes
86
+ # a hash that matches anima defined attributes
87
+ #
88
+ # @return [undefined]
89
+ #
90
+ # rubocop:disable Lint/MissingSuper
91
+ def initialize(attributes)
92
+ self.class.anima.initialize_instance(self, attributes)
93
+ end
94
+ # rubocop:enable Lint/MissingSuper
95
+
96
+ # Return a hash representation of an anima infected object
97
+ #
98
+ # @example
99
+ # anima.to_h # => { :foo => : bar }
100
+ #
101
+ # @return [Hash]
102
+ #
103
+ # @api public
104
+ def to_h
105
+ self.class.anima.attributes_hash(self)
106
+ end
107
+
108
+ # Return updated instance
109
+ #
110
+ # @example
111
+ # klass = Class.new do
112
+ # include Anima.new(:foo, :bar)
113
+ # end
114
+ #
115
+ # foo = klass.new(:foo => 1, :bar => 2)
116
+ # updated = foo.with(:foo => 3)
117
+ # updated.foo # => 3
118
+ # updated.bar # => 2
119
+ #
120
+ # @param [Hash] attributes
121
+ #
122
+ # @return [Anima]
123
+ #
124
+ # @api public
125
+ def with(attributes)
126
+ self.class.new(to_h.update(attributes))
127
+ end
128
+ end # InstanceMethods
129
+
130
+ private
131
+
132
+ # Infect the instance with anima
133
+ #
134
+ # @param [Class, Module] scope
135
+ #
136
+ # @return [undefined]
137
+ def included(descendant)
138
+ descendant.instance_exec(self, attribute_names) do |anima, names|
139
+ # Define anima method
140
+ define_singleton_method(:anima) { anima }
141
+
142
+ # Define instance methods
143
+ include InstanceMethods
144
+
145
+ # Define attribute readers
146
+ attr_reader(*names)
147
+
148
+ # Define equalizer
149
+ include Equalizer.new(*names)
150
+ end
151
+ end
152
+
153
+ # Fail unless keys in +attribute_hash+ matches #attribute_names
154
+ #
155
+ # @param [Class] klass
156
+ # the class being initialized
157
+ #
158
+ # @param [Hash] attribute_hash
159
+ # the attributes to initialize +object+ with
160
+ #
161
+ # @return [undefined]
162
+ #
163
+ # @raise [Error]
164
+ def assert_known_attributes(klass, attribute_hash)
165
+ keys = attribute_hash.keys
166
+
167
+ unknown = keys - attribute_names
168
+ missing = attribute_names - keys
169
+
170
+ unless unknown.empty? && missing.empty?
171
+ fail Error.new(klass, missing, unknown)
172
+ end
173
+ end
174
+
175
+ # Return new instance
176
+ #
177
+ # @param [Enumerable<Symbol>] attributes
178
+ #
179
+ # @return [Anima]
180
+ def new(attributes)
181
+ self.class.new(*attributes)
182
+ end
183
+ end # Anima
184
+ 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
@@ -3,7 +3,6 @@
3
3
  module Unparser
4
4
  # Namespace for AST processing tools
5
5
  module AST
6
-
7
6
  FIRST_CHILD = ->(node) { node.children.first }.freeze
8
7
  TAUTOLOGY = ->(_node) { true }.freeze
9
8
 
@@ -79,7 +78,7 @@ module Unparser
79
78
 
80
79
  # AST enumerator
81
80
  class Enumerator
82
- include Adamantium::Flat, Concord.new(:node, :controller), Enumerable
81
+ include Adamantium, Concord.new(:node, :controller), Enumerable
83
82
 
84
83
  # Return new instance
85
84
  #
@@ -3,7 +3,7 @@
3
3
  module Unparser
4
4
  # Class to colorize strings
5
5
  class Color
6
- include Adamantium::Flat, Concord.new(:code)
6
+ include Adamantium, Concord.new(:code)
7
7
 
8
8
  # Format text with color
9
9
  #
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # A mixin to define a composition
5
+ #
6
+ # Original code before vendoring and reduction from: https://github.com/mbj/concord.
7
+ class Concord < Module
8
+ include Adamantium, Equalizer.new(:names)
9
+
10
+ # The maximum number of objects the hosting class is composed of
11
+ MAX_NR_OF_OBJECTS = 3
12
+
13
+ # Return names
14
+ #
15
+ # @return [Enumerable<Symbol>]
16
+ #
17
+ # @api private
18
+ #
19
+ attr_reader :names
20
+
21
+ private
22
+
23
+ # Initialize object
24
+ #
25
+ # @return [undefined]
26
+ #
27
+ # @api private
28
+ #
29
+ # rubocop:disable Lint/MissingSuper
30
+ def initialize(*names)
31
+ if names.length > MAX_NR_OF_OBJECTS
32
+ fail "Composition of more than #{MAX_NR_OF_OBJECTS} objects is not allowed"
33
+ end
34
+
35
+ @names = names
36
+ define_initialize
37
+ define_readers
38
+ define_equalizer
39
+ end
40
+ # rubocop:enable Lint/MissingSuper
41
+
42
+ # Define equalizer
43
+ #
44
+ # @return [undefined]
45
+ #
46
+ # @api private
47
+ #
48
+ def define_equalizer
49
+ include(Equalizer.new(*names))
50
+ end
51
+
52
+ # Define readers
53
+ #
54
+ # @return [undefined]
55
+ #
56
+ # @api private
57
+ #
58
+ def define_readers
59
+ attribute_names = names
60
+ attr_reader(*attribute_names)
61
+
62
+ protected(*attribute_names) if attribute_names.any?
63
+ end
64
+
65
+ # Define initialize method
66
+ #
67
+ # @return [undefined]
68
+ #
69
+ # @api private
70
+ #
71
+ #
72
+ def define_initialize
73
+ ivars = instance_variable_names
74
+ size = names.size
75
+
76
+ define_method :initialize do |*args|
77
+ args_size = args.size
78
+ unless args_size.equal?(size)
79
+ fail ArgumentError, "wrong number of arguments (#{args_size} for #{size})"
80
+ end
81
+
82
+ ivars.zip(args) { |ivar, arg| instance_variable_set(ivar, arg) }
83
+ end
84
+ end
85
+
86
+ # Return instance variable names
87
+ #
88
+ # @return [String]
89
+ #
90
+ # @api private
91
+ #
92
+ def instance_variable_names
93
+ names.map { |name| "@#{name}" }
94
+ end
95
+
96
+ # Mixin for public attribute readers
97
+ class Public < self
98
+
99
+ # Hook called when module is included
100
+ #
101
+ # @param [Class,Module] descendant
102
+ #
103
+ # @return [undefined]
104
+ #
105
+ # @api private
106
+ #
107
+ def included(descendant)
108
+ names.each do |name|
109
+ descendant.__send__(:public, name)
110
+ end
111
+ end
112
+ end # Public
113
+ end # Concord
114
+ end # Unparser
@@ -3,7 +3,7 @@
3
3
  module Unparser
4
4
  # Class to create diffs from source code
5
5
  class Diff
6
- include Adamantium::Flat, Concord.new(:old, :new)
6
+ include Adamantium, Concord.new(:old, :new)
7
7
 
8
8
  ADDITION = '+'
9
9
  DELETION = '-'
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module RequireBlock
5
+
6
+ private
7
+
8
+ # Raise error unless block is provided
9
+ #
10
+ # @raise [MissingBlockError]
11
+ # if no block is given
12
+ #
13
+ # @return [self]
14
+ def require_block
15
+ fail LocalJumpError unless block_given?
16
+
17
+ self
18
+ end
19
+ end # RequireBLock
20
+
21
+ class Either
22
+ include(
23
+ Adamantium,
24
+ Concord.new(:value),
25
+ RequireBlock
26
+ )
27
+
28
+ # Execute block and wrap error in left
29
+ #
30
+ # @param [Class<Exception>] exception
31
+ #
32
+ # @return [Either<Exception, Object>]
33
+ def self.wrap_error(*exceptions)
34
+ Right.new(yield)
35
+ rescue *exceptions => error
36
+ Left.new(error)
37
+ end
38
+
39
+ # Test for left constructor
40
+ #
41
+ # @return [Boolean]
42
+ def left?
43
+ instance_of?(Left)
44
+ end
45
+
46
+ # Test for right constructor
47
+ #
48
+ # @return [Boolean]
49
+ def right?
50
+ instance_of?(Right)
51
+ end
52
+
53
+ class Left < self
54
+ # Evaluate functor block
55
+ #
56
+ # @return [Either::Left<Object>]
57
+ def fmap(&block)
58
+ require_block(&block)
59
+ end
60
+
61
+ # Evaluate applicative block
62
+ #
63
+ # @return [Either::Left<Object>]
64
+ def bind(&block)
65
+ require_block(&block)
66
+ end
67
+
68
+ # Unwrap value from left
69
+ #
70
+ # @return [Object]
71
+ def from_left
72
+ value
73
+ end
74
+
75
+ # Unwrap value from right
76
+ #
77
+ # @return [Object]
78
+ #
79
+ def from_right
80
+ if block_given?
81
+ yield(value)
82
+ else
83
+ fail "Expected right value, got #{inspect}"
84
+ end
85
+ end
86
+
87
+ # Map over left value
88
+ #
89
+ # @return [Either::Right<Object>]
90
+ def lmap
91
+ Left.new(yield(value))
92
+ end
93
+
94
+ # Evaluate left side of branch
95
+ #
96
+ # @param [#call] left
97
+ # @param [#call] _right
98
+ def either(left, _right)
99
+ left.call(value)
100
+ end
101
+ end # Left
102
+
103
+ class Right < self
104
+ # Evaluate functor block
105
+ #
106
+ # @return [Either::Right<Object>]
107
+ def fmap
108
+ Right.new(yield(value))
109
+ end
110
+
111
+ # Evaluate applicative block
112
+ #
113
+ # @return [Either<Object>]
114
+ def bind
115
+ yield(value)
116
+ end
117
+
118
+ # Unwrap value from left
119
+ #
120
+ # @return [Object]
121
+ #
122
+ def from_left
123
+ if block_given?
124
+ yield(value)
125
+ else
126
+ fail "Expected left value, got #{inspect}"
127
+ end
128
+ end
129
+
130
+ # Unwrap value from right
131
+ #
132
+ # @return [Object]
133
+ def from_right
134
+ value
135
+ end
136
+
137
+ # Map over left value
138
+ #
139
+ # @return [Either::Right<Object>]
140
+ def lmap(&block)
141
+ require_block(&block)
142
+ end
143
+
144
+ # Evaluate right side of branch
145
+ #
146
+ # @param [#call] _left
147
+ # @param [#call] right
148
+ def either(_left, right)
149
+ right.call(value)
150
+ end
151
+ end # Right
152
+ end # Either
153
+ end # Unparser