eavi 2.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 89417f14ff76e2a1a025877ffc8f421da4efe828
4
+ data.tar.gz: f80ca41d54257abd0c2abd75943b29effce8fb2d
5
+ SHA512:
6
+ metadata.gz: 616c403446a6fae16c540b058bee183a57de7884b9b749eba4b6aaac78786e4503b5dc438e3650a5d63ee8ddf76a4c07d20d59ceecb2c4f3bd3fa9b89e798aea
7
+ data.tar.gz: beb4d9dff7a5833de44466f2ad383b3774cd3a6d0b0c06bfc5ceb12dd83e423e43751f7270b2ab5b026bf8dbc5f364db55c631a06650b1c3f15e37ab1cdecb7b
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Pierre Le Gall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ module Eavi
2
+ # Error raised when a Visitor do not have a visit method to handle an object.
3
+ class NoVisitMethodError < TypeError
4
+ attr_reader :visitor, :visited, :visited_as
5
+
6
+ def initialize(visitor, visited, visited_as)
7
+ @visitor = visitor
8
+ @visited = visited
9
+ @visited_as = visited_as
10
+ end
11
+
12
+ def to_s
13
+ "no visit method in #{@visitor} for #{@visited_as} instances"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ module Eavi
2
+ # Helper for visit methods generation, matching & co.
3
+ module VisitMethodHelper
4
+ TEMPLATE = 'visit[%s]'.freeze
5
+ REGEXP = /^visit\[(.*)\]$/
6
+
7
+ # Return a visit method name for the type +type+.
8
+ def self.gen_name(type)
9
+ return TEMPLATE % type.name
10
+ end
11
+
12
+ # Return true if the +visit_method_name+ is a well formed
13
+ # visit method name, else false.
14
+ def self.match(visit_method_name)
15
+ return REGEXP.match(visit_method_name)
16
+ end
17
+
18
+ # Return the type matching a visit method.
19
+ def self.get_type(visit_method)
20
+ type_symbol = match(visit_method).captures[0]
21
+ return const_get(type_symbol)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,131 @@
1
+ require_relative 'visit_method_helper'
2
+ require_relative 'no_visit_method_error'
3
+
4
+ module Eavi
5
+ # Extend a module/class or include a class with Visitor
6
+ # to make it a dynamic visitor (see the OOP visitor pattern).
7
+ module Visitor
8
+ # Call the visit method associated with the type of +object+.
9
+ def visit(object, *args, as: object.class)
10
+ as.ancestors.each do |type|
11
+ visit_method_name = VisitMethodHelper.gen_name(type)
12
+ next unless respond_to?(visit_method_name)
13
+ return send(visit_method_name, object, *args)
14
+ end
15
+ raise NoVisitMethodError.new(self, object, as)
16
+ end
17
+
18
+ class << self
19
+ def included(visitor)
20
+ visitor.extend(ModuleDSL)
21
+ visitor.extend(ModuleMethods)
22
+ visitor.extend(ModuleMethodsWhenIncluded)
23
+ end
24
+
25
+ def extended(visitor)
26
+ visitor.extend(ModuleDSL)
27
+ visitor.extend(ModuleMethods)
28
+ visitor.extend(ModuleMethodsWhenExtended)
29
+ end
30
+ end
31
+
32
+ # Domain-Specific Language for the module/class
33
+ module ModuleDSL
34
+ # DSL method to add visit methods on types +types+.
35
+ def def_visit(*types, &block)
36
+ add_visit_method(*types, &block)
37
+ end
38
+
39
+ # DSL method to remove visit methods on types +types+.
40
+ def undef_visit(*types)
41
+ remove_visit_method(*types)
42
+ end
43
+ end
44
+
45
+ # Extends if included or extended
46
+ module ModuleMethods
47
+ # Alias the `visit` method.
48
+ def alias_visit_method(visit_method_alias)
49
+ specialized_alias_visit_method(visit_method_alias)
50
+ end
51
+
52
+ # Add/override a visit method for the types +types+.
53
+ def add_visit_method(*types, &block)
54
+ block = block.curry(1) if block.arity.zero?
55
+ types.each do |type|
56
+ specialized_add_visit_method(type, &block)
57
+ end
58
+ end
59
+
60
+ # Remove the visit methods for the types +types+.
61
+ def remove_visit_method(*types)
62
+ types.each do |type|
63
+ specialized_remove_visit_method(type)
64
+ end
65
+ end
66
+
67
+ # Remove all the visit methods.
68
+ def reset_visit_methods
69
+ visit_methods.each do |visit_method|
70
+ specialized_remove_method(visit_method)
71
+ end
72
+ end
73
+
74
+ # Return a list of the visit method.
75
+ def visit_methods
76
+ return methods.select do |method|
77
+ VisitMethodHelper.match(method)
78
+ end
79
+ end
80
+
81
+ # Return a list of the types with a visit method.
82
+ def visitable_types
83
+ return visit_methods.collect do |visit_method|
84
+ VisitMethodHelper.get_type(visit_method)
85
+ end
86
+ end
87
+ end
88
+
89
+ # Extends only when included
90
+ module ModuleMethodsWhenIncluded
91
+ private
92
+
93
+ def specialized_alias_visit_method(visit_method_alias)
94
+ define_method(visit_method_alias, instance_method(:visit))
95
+ end
96
+
97
+ def specialized_add_visit_method(type, &block)
98
+ define_method(VisitMethodHelper.gen_name(type), block)
99
+ end
100
+
101
+ def specialized_remove_visit_method(type)
102
+ remove_method(VisitMethodHelper.gen_name(type))
103
+ end
104
+
105
+ def specialized_remove_method(visit_method)
106
+ remove_method(visit_method)
107
+ end
108
+ end
109
+
110
+ # Extends only when extended
111
+ module ModuleMethodsWhenExtended
112
+ private
113
+
114
+ def specialized_alias_visit_method(visit_method_alias)
115
+ define_singleton_method(visit_method_alias, method(:visit))
116
+ end
117
+
118
+ def specialized_add_visit_method(type, &block)
119
+ define_singleton_method(VisitMethodHelper.gen_name(type), block)
120
+ end
121
+
122
+ def specialized_remove_visit_method(type)
123
+ singleton_class.send(:remove_method, VisitMethodHelper.gen_name(type))
124
+ end
125
+
126
+ def specialized_remove_method(visit_method)
127
+ singleton_class.send(:remove_method, visit_method)
128
+ end
129
+ end
130
+ end
131
+ end
data/lib/eavi.rb ADDED
@@ -0,0 +1,5 @@
1
+ # Eavi is a Ruby visitor pattern helper.
2
+ module Eavi
3
+ end
4
+
5
+ require_relative 'eavi/visitor'
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eavi
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Pierre Le Gall
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Make the visitor pattern accessible and flexible in Ruby.
14
+ email: pierre@legall.im
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - LICENSE
20
+ - lib/eavi.rb
21
+ - lib/eavi/no_visit_method_error.rb
22
+ - lib/eavi/visit_method_helper.rb
23
+ - lib/eavi/visitor.rb
24
+ homepage: http://github.com/lepieru/eavi
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.5.1
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: A visitor pattern helper for Ruby.
48
+ test_files: []