risitor 1.0.1
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 +7 -0
- data/lib/risitor/no_visit_method_error.rb +16 -0
- data/lib/risitor/visit_method_helper.rb +24 -0
- data/lib/risitor/visitor.rb +121 -0
- data/lib/risitor.rb +5 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f83e529945dc5da5ac952f4e85b6eb454f4b0e2c
|
4
|
+
data.tar.gz: e2cba098ab838db018aeef47091e8d16bd5bb2e5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 48653f25fe5a6c89dd95c86ee16198be29c69edfa0a5b160ad3b10833f97517ba7f86ab0e1abb287f2807f02c622ad4ca3afe6aa1ef3ef966e42c336101950f0
|
7
|
+
data.tar.gz: 97a61929cba44373c0ee32e6c7acd490ad5fc9c19e3343c44524d1e079494d2ee4c9c74640fd4e38763c24e68303c0a23d8ad69528833288a6906a68239652e4
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Risitor
|
2
|
+
# Error raised when a Visitor failed to visit an object.
|
3
|
+
class NoVisitMethodError < NoMethodError
|
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 message
|
13
|
+
"no method in #{@visitor} to visit as #{@visited_as}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Risitor
|
2
|
+
# Helper for visit methods generation, matching & co.
|
3
|
+
module VisitMethodHelper
|
4
|
+
TEMPLATE = "visit[%s]"
|
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,121 @@
|
|
1
|
+
require_relative './no_visit_method_error'
|
2
|
+
require_relative './visit_method_helper'
|
3
|
+
|
4
|
+
module Risitor
|
5
|
+
# The Visitor module can extend a module or include a class
|
6
|
+
# to make it a dynamic visitor (see the OOP visitor pattern).
|
7
|
+
module Visitor
|
8
|
+
# Calling visit execute the method associated with the
|
9
|
+
# type of +object+.
|
10
|
+
def visit(object, *args, as: object.class)
|
11
|
+
as.ancestors.each do |type|
|
12
|
+
visit_method = VisitMethodHelper.gen_name(type)
|
13
|
+
next unless respond_to? visit_method
|
14
|
+
return send(visit_method, object, *args)
|
15
|
+
end
|
16
|
+
raise NoVisitMethodError.new(self, object, as)
|
17
|
+
end
|
18
|
+
|
19
|
+
# List of the methods extended by a Visitor
|
20
|
+
module ClassMethods
|
21
|
+
# Alias the `visit` method
|
22
|
+
def alias_visit_method(visit_method_alias)
|
23
|
+
define_new_visit_method(visit_method_alias)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Add/override a visit method for the types +types+.
|
27
|
+
def add_visit_method(*types, &block)
|
28
|
+
block = block.curry(1) if block.arity == 0
|
29
|
+
types.each do |type|
|
30
|
+
define_visit_method_for type, &block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Remove the visit methods for the types +types+.
|
35
|
+
def remove_visit_method(*types)
|
36
|
+
types.each do |type|
|
37
|
+
undefine_visit_method_for type
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Remove all the visit methods.
|
42
|
+
def reset_visit_methods
|
43
|
+
visit_methods.each do |visit_method|
|
44
|
+
undefine_visit_method visit_method
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return a list of the visit method.
|
49
|
+
def visit_methods
|
50
|
+
return methods.select do |method|
|
51
|
+
VisitMethodHelper.match method
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return a list of the types with a visit method.
|
56
|
+
def visitable_types
|
57
|
+
return visit_methods.collect do |visit_method|
|
58
|
+
VisitMethodHelper.get_type(visit_method)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
alias_method :when_visiting, :add_visit_method
|
63
|
+
end
|
64
|
+
|
65
|
+
# List of the methods extended by a Visitor when included.
|
66
|
+
module ClassMethodsWhenIncluded
|
67
|
+
private
|
68
|
+
|
69
|
+
def define_new_visit_method(new_visit_method)
|
70
|
+
define_method new_visit_method, instance_method(:visit)
|
71
|
+
end
|
72
|
+
|
73
|
+
def define_visit_method_for(klass, &block)
|
74
|
+
define_method VisitMethodHelper.gen_name(klass), block
|
75
|
+
end
|
76
|
+
|
77
|
+
def undefine_visit_method_for(klass)
|
78
|
+
remove_method VisitMethodHelper.gen_name(klass)
|
79
|
+
end
|
80
|
+
|
81
|
+
def undefine_visit_method(visit_method)
|
82
|
+
remove_method visit_method
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# List of the methods extended by a Visitor when included.
|
87
|
+
module ClassMethodsWhenExtended
|
88
|
+
private
|
89
|
+
|
90
|
+
def define_new_visit_method(new_visit_method)
|
91
|
+
define_singleton_method new_visit_method, method(:visit)
|
92
|
+
end
|
93
|
+
|
94
|
+
def define_visit_method_for(klass, &block)
|
95
|
+
define_singleton_method VisitMethodHelper.gen_name(klass), block
|
96
|
+
end
|
97
|
+
|
98
|
+
def undefine_visit_method_for(klass)
|
99
|
+
self.singleton_class.send :remove_method, VisitMethodHelper.gen_name(klass)
|
100
|
+
end
|
101
|
+
|
102
|
+
def undefine_visit_method(visit_method)
|
103
|
+
self.singleton_class.send :remove_method, visit_method
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class << self
|
108
|
+
def included(visitor)
|
109
|
+
visitor.extend ClassMethods
|
110
|
+
visitor.extend ClassMethodsWhenIncluded
|
111
|
+
end
|
112
|
+
|
113
|
+
def extended(visitor)
|
114
|
+
visitor.extend ClassMethods
|
115
|
+
visitor.extend ClassMethodsWhenExtended
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
Base = Visitor
|
121
|
+
end
|
data/lib/risitor.rb
ADDED
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: risitor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pierre Le Gall
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Make the visitor pattern accessible and flexible inside Ruby.
|
14
|
+
email: pierre@legall.im
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/risitor.rb
|
20
|
+
- lib/risitor/no_visit_method_error.rb
|
21
|
+
- lib/risitor/visit_method_helper.rb
|
22
|
+
- lib/risitor/visitor.rb
|
23
|
+
homepage: http://github.com/lepieru/risitor
|
24
|
+
licenses:
|
25
|
+
- MIT
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.5.1
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: A visitor pattern helper for Ruby.
|
47
|
+
test_files: []
|