seat-belt 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +29 -0
- data/.travis.yml +6 -0
- data/Changelog.md +55 -0
- data/Gemfile +15 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +705 -0
- data/Rakefile +1 -0
- data/ext/.gitkeep +0 -0
- data/lib/seatbelt.rb +37 -0
- data/lib/seatbelt/collections/collection.rb +56 -0
- data/lib/seatbelt/core.rb +15 -0
- data/lib/seatbelt/core/callee.rb +35 -0
- data/lib/seatbelt/core/eigenmethod.rb +150 -0
- data/lib/seatbelt/core/eigenmethod_proxy.rb +45 -0
- data/lib/seatbelt/core/ext/core_ext.rb +0 -0
- data/lib/seatbelt/core/gate.rb +198 -0
- data/lib/seatbelt/core/ghost_tunnel.rb +81 -0
- data/lib/seatbelt/core/implementation.rb +158 -0
- data/lib/seatbelt/core/interface.rb +51 -0
- data/lib/seatbelt/core/iterators/method_config.rb +50 -0
- data/lib/seatbelt/core/lookup_table.rb +101 -0
- data/lib/seatbelt/core/pool.rb +90 -0
- data/lib/seatbelt/core/property.rb +161 -0
- data/lib/seatbelt/core/proxy.rb +135 -0
- data/lib/seatbelt/core/synthesizeable.rb +50 -0
- data/lib/seatbelt/core/terminal.rb +59 -0
- data/lib/seatbelt/dependencies.rb +5 -0
- data/lib/seatbelt/document.rb +175 -0
- data/lib/seatbelt/errors.rb +1 -0
- data/lib/seatbelt/errors/errors.rb +150 -0
- data/lib/seatbelt/gate_config.rb +59 -0
- data/lib/seatbelt/ghost.rb +140 -0
- data/lib/seatbelt/models.rb +9 -0
- data/lib/seatbelt/seatbelt.rb +10 -0
- data/lib/seatbelt/synthesizer.rb +3 -0
- data/lib/seatbelt/synthesizers/document.rb +16 -0
- data/lib/seatbelt/synthesizers/mongoid.rb +16 -0
- data/lib/seatbelt/synthesizers/synthesizer.rb +146 -0
- data/lib/seatbelt/tape.rb +2 -0
- data/lib/seatbelt/tape_deck.rb +71 -0
- data/lib/seatbelt/tapes/tape.rb +105 -0
- data/lib/seatbelt/tapes/util/delegate.rb +56 -0
- data/lib/seatbelt/translator.rb +66 -0
- data/lib/seatbelt/version.rb +3 -0
- data/seatbelt.gemspec +27 -0
- data/spec/lib/seatbelt/core/eigenmethod_spec.rb +102 -0
- data/spec/lib/seatbelt/core/gate_spec.rb +521 -0
- data/spec/lib/seatbelt/core/ghost_tunnel_spec.rb +21 -0
- data/spec/lib/seatbelt/core/lookup_table_spec.rb +234 -0
- data/spec/lib/seatbelt/core/pool_spec.rb +270 -0
- data/spec/lib/seatbelt/core/proxy_spec.rb +108 -0
- data/spec/lib/seatbelt/core/terminal_spec.rb +184 -0
- data/spec/lib/seatbelt/document_spec.rb +287 -0
- data/spec/lib/seatbelt/gate_config_spec.rb +98 -0
- data/spec/lib/seatbelt/ghost_spec.rb +568 -0
- data/spec/lib/seatbelt/synthesizers/document_spec.rb +47 -0
- data/spec/lib/seatbelt/synthesizers/mongoid_spec.rb +134 -0
- data/spec/lib/seatbelt/synthesizers/synthesizer_spec.rb +112 -0
- data/spec/lib/seatbelt/tape_deck_spec.rb +180 -0
- data/spec/lib/seatbelt/tapes/tape_spec.rb +115 -0
- data/spec/lib/seatbelt/translator_spec.rb +108 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/implementations/seatbelt_environment.rb +19 -0
- data/spec/support/shared_examples/shared_api_class.rb +7 -0
- data/spec/support/shared_examples/shared_collection_child.rb +7 -0
- data/spec/support/worlds/eigenmethod_world.rb +7 -0
- metadata +205 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
module Seatbelt
|
2
|
+
|
3
|
+
# Public: Provides switches to enable accessing the implementation instance
|
4
|
+
# of an API class instances.
|
5
|
+
#
|
6
|
+
# Any API class that implements Seatbelt::Ghost can have access to its
|
7
|
+
# implementation class instance. This behaviour has to be enabled before using
|
8
|
+
# because its a violation of the Public/Private API approach.
|
9
|
+
#
|
10
|
+
# (And yes - in Ruby private methods are not really private methods.)
|
11
|
+
#
|
12
|
+
# Accessing the implementation instance is only available after the API Class
|
13
|
+
# was instantiated.
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
#
|
17
|
+
# class Hotel
|
18
|
+
# include Seatbelt::Ghost
|
19
|
+
#
|
20
|
+
# enable_tunneling! # access to the implementation instance is not
|
21
|
+
# # possible.
|
22
|
+
#
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class ImplementationHotel
|
26
|
+
# include Seatbelt::Document
|
27
|
+
# include Seatbelt::Gate
|
28
|
+
#
|
29
|
+
# attribute :ignore_dirty_rooms, Boolean
|
30
|
+
#
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# hotel = new Hotel
|
34
|
+
# hotel.tunnel(:ignore_dirty_rooms=,false)
|
35
|
+
#
|
36
|
+
# Passing blocks is also available if the accessed method supports blocks
|
37
|
+
#
|
38
|
+
# class ImplementationHotel
|
39
|
+
# include Seatbelt::Document
|
40
|
+
# include Seatbelt::Gate
|
41
|
+
#
|
42
|
+
# attribute :ignore_dirty_rooms, Boolean
|
43
|
+
#
|
44
|
+
# def filter_rooms(sections)
|
45
|
+
# rooms = self.rooms.map{|room| sections.include?(room_type)}
|
46
|
+
# yield(rooms)
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# hotel.tunnel(:filter_rooms, ["shower, kitchen"]) do |rooms|
|
51
|
+
# rooms.select do |room|
|
52
|
+
# # do something
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
module GhostTunnel
|
56
|
+
extend self
|
57
|
+
|
58
|
+
# Public: Enables tunnel support for an instance of API class.
|
59
|
+
#
|
60
|
+
# Defines the #tunnel method.
|
61
|
+
def enable_tunneling!
|
62
|
+
define_method :tunnel do |name, *args, &block|
|
63
|
+
callees = self.eigenmethods.map{|n| n.instance_variable_get(:@callee)}
|
64
|
+
unless callees.empty?
|
65
|
+
callee = callees.first
|
66
|
+
callee.send(name, *args, &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Public: Disables tunnel support for an instance of API class.
|
72
|
+
#
|
73
|
+
# Removes the #tunnel method defined in #enable_tunneling! .
|
74
|
+
def disable_tunneling!
|
75
|
+
if self.instance_methods.include?(:tunnel)
|
76
|
+
remove_method(:tunnel)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Seatbelt
|
2
|
+
module Gate
|
3
|
+
module Implementation
|
4
|
+
|
5
|
+
# Internal: Helper array to store method iterationable values.
|
6
|
+
def bulk_methods
|
7
|
+
@bulk_methods ||= []
|
8
|
+
end
|
9
|
+
private :bulk_methods
|
10
|
+
# Public: Provides definition for which class and object level (instance,
|
11
|
+
# class) the implementation methods match the API class methods.
|
12
|
+
#
|
13
|
+
# namespace - The API class (with complete namespace)
|
14
|
+
# scope - The object level of 'namespace' [:instance, :class]
|
15
|
+
# &block - The block that contains the implementation match
|
16
|
+
# definitions.
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
#
|
20
|
+
# class ImplementHotel
|
21
|
+
# include Seatbelt::Gate
|
22
|
+
#
|
23
|
+
# implementation "Hotel", :class do
|
24
|
+
# match 'implement_find_region' => 'find_nearby'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# def implement_find_region(options)
|
28
|
+
# #....
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
def implementation(namespace, scope, &block)
|
33
|
+
methods = []
|
34
|
+
@_namespace = namespace
|
35
|
+
@_scope = Seatbelt::GateConfig.method_directives[scope]
|
36
|
+
yield(self)
|
37
|
+
iterator = Core::Iterators::MethodConfig.
|
38
|
+
send("array_method_iterator",@_namespace,
|
39
|
+
self,scope)
|
40
|
+
bulk_methods.each(&iterator)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Builds an Hash representation that is used by the method_added
|
44
|
+
# hook (Seatbelt::Gate::Classmethods) to find the implementation methods
|
45
|
+
# of a specific object level.
|
46
|
+
#
|
47
|
+
# If the argument includes a :superclass key with a truthy value,
|
48
|
+
# #implementation_from_superclass is called to simulate similiar behaviour
|
49
|
+
# to the method added hook.
|
50
|
+
#
|
51
|
+
# Examples:
|
52
|
+
#
|
53
|
+
# implementation "Book", :instance do
|
54
|
+
# match 'implementation_publishing' => 'publishing'
|
55
|
+
# match 'implementation_paper_type' => 'paper_type',
|
56
|
+
# :superclass => true
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# hsh - An Hash containing the implementation method name as key and
|
60
|
+
# the API method as value.
|
61
|
+
# :superclass - defines that there will be an extra lookup for an
|
62
|
+
# implementation method in the class' superclass.
|
63
|
+
# (defaults to false)
|
64
|
+
def match(hsh)
|
65
|
+
hsh.stringify_keys!
|
66
|
+
superclass_definition = hsh.fetch("superclass", false)
|
67
|
+
delegate = hsh.fetch("delegated", false)
|
68
|
+
hsh.delete("superclass")
|
69
|
+
implementation_method = hsh.keys.first.to_sym
|
70
|
+
remote_method = hsh.values.first
|
71
|
+
if superclass_definition
|
72
|
+
config = {:as => "#{@_namespace}#{@_scope}#{remote_method}"}
|
73
|
+
self.send(:implementation_from_superclass,
|
74
|
+
implementation_method, config)
|
75
|
+
end
|
76
|
+
imp_hsh = {
|
77
|
+
implementation_method => {:as => "#{@_scope}#{remote_method}"}
|
78
|
+
}
|
79
|
+
bulk_methods << imp_hsh
|
80
|
+
if delegate && @_scope.eql?("#")
|
81
|
+
notify_delgated_method(implementation_method, imp_hsh, remote_method)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Public: Builds an Hash representation for property that is passed to
|
86
|
+
# #match(hsh)
|
87
|
+
#
|
88
|
+
# A property is an ivar that is accessible through a getter and setter.
|
89
|
+
#
|
90
|
+
# #match_property is only useable within the :instance scope,
|
91
|
+
#
|
92
|
+
# Examples:
|
93
|
+
#
|
94
|
+
# # Having an identical property wthin the interface.
|
95
|
+
# implementation "Book", :instance do
|
96
|
+
# match_property 'author'
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# # Property names differ
|
100
|
+
# implementation "Book", :instance do
|
101
|
+
# match_property 'implementation_title' => 'title'
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# # Property is defined in the superclass
|
105
|
+
# implementation "Novel", :instance do
|
106
|
+
# match_property :publisher, :superclass => true
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# args - An argumentlist containing one of the following
|
110
|
+
# - property name as String or Symbol
|
111
|
+
# - config Hash similiar to #match
|
112
|
+
# - an Hash containing :superclass key (optional)
|
113
|
+
#
|
114
|
+
def match_property(*args)
|
115
|
+
options = {}
|
116
|
+
if args.size.eql?(1)
|
117
|
+
property = args.pop
|
118
|
+
else
|
119
|
+
options = args.pop
|
120
|
+
property = args.shift
|
121
|
+
options.stringify_keys!
|
122
|
+
end
|
123
|
+
if property.is_a?(String) || property.is_a?(Symbol)
|
124
|
+
[property, "#{property}="].each do |method|
|
125
|
+
match_options = {}
|
126
|
+
match_options[method] = method
|
127
|
+
match_options["superclass"] = options.fetch("superclass", false)
|
128
|
+
match(match_options)
|
129
|
+
end
|
130
|
+
elsif property.is_a?(Hash)
|
131
|
+
property.stringify_keys!
|
132
|
+
implement_property = property.keys.first.to_sym
|
133
|
+
remote_property = property.values.first
|
134
|
+
[{implement_property => remote_property},
|
135
|
+
{"#{implement_property}=" => "#{remote_property}="}].each do |opt|
|
136
|
+
if property.has_key?("superclass")
|
137
|
+
opt["superclass"] = property.fetch("superclass")
|
138
|
+
end
|
139
|
+
match(opt)
|
140
|
+
end
|
141
|
+
else
|
142
|
+
raise Seatbelt::Errors::TypeMissmatchError.
|
143
|
+
new("String, Symbol or Hash",
|
144
|
+
property.class.name)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def notify_delgated_method(implementation_method, imp_hsh, remote_method)
|
151
|
+
namespaced_scope = "#{@_namespace}#{@_scope}#{remote_method}"
|
152
|
+
bulk_methods.last[implementation_method][:as] = namespaced_scope
|
153
|
+
bulk_methods.last[implementation_method][:delegated] = true
|
154
|
+
mark_as_instance_implementation.call(imp_hsh, implementation_method)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Seatbelt
|
2
|
+
module Interface
|
3
|
+
extend self
|
4
|
+
include Seatbelt::Property::ClassMethods
|
5
|
+
# Public: Provides definitions of API Meta methods. For more informations
|
6
|
+
# of API meta methods see Seatbelt::Pool::Api#api_method
|
7
|
+
#
|
8
|
+
# scope - the type the methods should be defined for [:instance, :class]
|
9
|
+
#
|
10
|
+
# &block - The block that contains the API method definitions.
|
11
|
+
#
|
12
|
+
# Example
|
13
|
+
#
|
14
|
+
# class Hotel
|
15
|
+
# include Seatbelt::Ghost
|
16
|
+
#
|
17
|
+
# interface :class do
|
18
|
+
# define :find_nearby,
|
19
|
+
# :block_required => false,
|
20
|
+
# :args => [:options]
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
def interface(scope, &block)
|
25
|
+
@scope = scope
|
26
|
+
yield(self) if block
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Defines an API method. This is only working if its called within
|
30
|
+
# the #interface method block.
|
31
|
+
#
|
32
|
+
# Wraps Seatbelt::Pool::Api#api_method
|
33
|
+
#
|
34
|
+
# name - Name of the API method
|
35
|
+
# hsh - an options Hash that refines the methods usage:
|
36
|
+
# :scope - the method type
|
37
|
+
# [:instance, :class]
|
38
|
+
# defaults to :instance
|
39
|
+
# :block_required - defines if the method
|
40
|
+
# require a Ruby Block for
|
41
|
+
# usage.
|
42
|
+
# :args - An array of expected
|
43
|
+
# arguments as Symbols or
|
44
|
+
# Strings. If omitted the
|
45
|
+
# method expects no arguments.
|
46
|
+
def define(name, hsh={})
|
47
|
+
hsh[:scope] = @scope
|
48
|
+
self.send(:api_method, name, hsh)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Seatbelt
|
2
|
+
module Core
|
3
|
+
module Iterators
|
4
|
+
|
5
|
+
# Public: Various methods useful for performing mathematical operations.
|
6
|
+
# All methods are module methods and should be called on the Math module.
|
7
|
+
#
|
8
|
+
class MethodConfig
|
9
|
+
|
10
|
+
|
11
|
+
# Public: Method config iterator block used by Gate#implement_class.
|
12
|
+
#
|
13
|
+
# klass - The API class name
|
14
|
+
# gate_klass - The class that includes the Gate module
|
15
|
+
#
|
16
|
+
# Returns a Proc that is useable for [].each
|
17
|
+
def self.array_method_iterator(klass, gate_klass, scope)
|
18
|
+
return lambda do |method_config|
|
19
|
+
method_config.send(:each_pair,
|
20
|
+
&hash_method_iterator(klass,gate_klass, scope))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# Public: Method config iterator block used by Gate#implement_class.
|
26
|
+
#
|
27
|
+
# klass - The API class name
|
28
|
+
# gate_klass - The class that includes the Gate module
|
29
|
+
#
|
30
|
+
# Returns a Proc that is useable for {}.each_pair
|
31
|
+
def self.hash_method_iterator(klass, gate_klass, scope)
|
32
|
+
methods_bucket = :implementation_methods if scope.eql?(:instance)
|
33
|
+
methods_bucket = :implementation_class_methods if scope.eql?(:class)
|
34
|
+
return lambda do |(key,value)|
|
35
|
+
method_name = key
|
36
|
+
implementation_config = {}
|
37
|
+
implementation_config[method_name] = {
|
38
|
+
:as => "#{klass}#{value[:as]}",
|
39
|
+
}
|
40
|
+
if value[:delegated]
|
41
|
+
implementation_config[method_name][:delegated] = value[:delegated]
|
42
|
+
end
|
43
|
+
gate_klass.send(methods_bucket) << implementation_config
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Seatbelt
|
2
|
+
|
3
|
+
# Public: Lookup table implementation that holds all meta methods for a
|
4
|
+
# Seatbelt API Class.
|
5
|
+
#
|
6
|
+
class LookupTable < Array
|
7
|
+
|
8
|
+
alias_method :add_method, :<<
|
9
|
+
alias_method :set, :<<
|
10
|
+
|
11
|
+
# Public: Removes a method configuration from the lookup table.
|
12
|
+
#
|
13
|
+
# method_name - The method name identifier as Symbol.
|
14
|
+
# scope - The scope in for removing the method, defaults to :instance
|
15
|
+
#
|
16
|
+
# Returns the removed method configuration if found otherwise nil.
|
17
|
+
def remove_method(method_name, scope: :instance)
|
18
|
+
method = find_method(method_name, scope: scope)
|
19
|
+
delete(method) if method
|
20
|
+
end
|
21
|
+
alias_method :unset, :remove_method
|
22
|
+
|
23
|
+
|
24
|
+
# Public: Finds a method configuration by method name.
|
25
|
+
#
|
26
|
+
# method_name - The method configuration identifier.
|
27
|
+
# scope - The scope to search for the method, defaults to :instance
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
#
|
31
|
+
# table = Seatbelt::LookupTable.new
|
32
|
+
# table.set({:my_method => {:scope => :klass, :block_required => true } })
|
33
|
+
#
|
34
|
+
# method = table.find_method(:my_method)
|
35
|
+
# #=> {:my_method => {:scope => :klass, :block_required => true } }
|
36
|
+
#
|
37
|
+
# Returns the method configuration if found, otherwise nil.
|
38
|
+
def find_method(method_name, scope: :instance)
|
39
|
+
detect do |method_config|
|
40
|
+
name = method_config.keys.first
|
41
|
+
method_scope = method_config[name][:scope]
|
42
|
+
name.eql?(method_name.to_sym) && method_scope.eql?(scope)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# Public: Gets a method configuration by name or configuration.
|
48
|
+
#
|
49
|
+
# method_c - The method identifier or its whole configurations Hash
|
50
|
+
# scope - The scope to search for the method, defaults to :instance
|
51
|
+
#
|
52
|
+
# Example:
|
53
|
+
#
|
54
|
+
# table = Seatbelt::LookupTable.new
|
55
|
+
# config = {
|
56
|
+
# :my_method => {
|
57
|
+
# :scope => :klass,
|
58
|
+
# :block_required => true
|
59
|
+
# }
|
60
|
+
# }
|
61
|
+
# table.set(config)
|
62
|
+
#
|
63
|
+
# method = table.get(:my_method)
|
64
|
+
# #=> {:my_method => {:scope => :klass, :block_required => true } }
|
65
|
+
#
|
66
|
+
# method = table.get(config)
|
67
|
+
# #=> {:my_method => {:scope => :klass, :block_required => true } }
|
68
|
+
#
|
69
|
+
# Returns the method configuration if found, otherwise nil.
|
70
|
+
def get(method_c, scope: :instance)
|
71
|
+
eqlCheck = false
|
72
|
+
identifier = if method_c.is_a?(Symbol) or method_c.is_a?(String) then
|
73
|
+
method_c
|
74
|
+
elsif method_c.is_a?(Hash)
|
75
|
+
scope = method_c.values.first[:scope]
|
76
|
+
eqlCheck = true
|
77
|
+
method_c.keys.first
|
78
|
+
end
|
79
|
+
method = find_method(identifier, scope: scope)
|
80
|
+
|
81
|
+
return method unless eqlCheck
|
82
|
+
return method if method.eql?(method_c)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Public: Check if the lookup table contains a method configuration.
|
87
|
+
#
|
88
|
+
# identifier - The method configuration identifier or the configuration
|
89
|
+
# scope - The scope to search for the method, defaults to :instance
|
90
|
+
#
|
91
|
+
# Returns true if table has method otherwise false.
|
92
|
+
def has?(identifier, scope: :instance)
|
93
|
+
if identifier.is_a?(Symbol) or identifier.is_a?(String)
|
94
|
+
return not(find_method(identifier, scope: scope).nil?)
|
95
|
+
elsif identifier.is_a?(Hash)
|
96
|
+
return not(get(identifier, scope: scope).nil?)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Seatbelt
|
2
|
+
module Pool
|
3
|
+
|
4
|
+
# Public: Provides storage of meta-method definitions.
|
5
|
+
# All classes that declare (API) meta-methods have to include this module
|
6
|
+
# by using the Seatbelt::Terminal wrapper.
|
7
|
+
#
|
8
|
+
# For API design matters: https://gist.github.com/dsci/a96048884fdff81aca68
|
9
|
+
module Api
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# Public: Registeres a meta-method at the method pool by adding the
|
13
|
+
# method configuration to a lookup table.
|
14
|
+
#
|
15
|
+
# Each class that includes Seatbelt::Terminal has its own lookup table.
|
16
|
+
#
|
17
|
+
# *args - An argument list consisting of
|
18
|
+
# * method_name - that has to be the first argument (required)
|
19
|
+
# * options - an options Hash that refines the methods usage:
|
20
|
+
# :scope - the method type
|
21
|
+
# [:instance, :class]
|
22
|
+
# defaults to :instance
|
23
|
+
# :block_required - defines if the method
|
24
|
+
# require a Ruby Block for
|
25
|
+
# usage.
|
26
|
+
# :args - An array of expected
|
27
|
+
# arguments as Symbols or
|
28
|
+
# Strings. If omitted the
|
29
|
+
# method expects no arguments.
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# If no arguments are passed, a Seatbelt::Errors::ArgumentsMissmatchError
|
33
|
+
# is raised.
|
34
|
+
# Raises a Seatbelt::Errors::MissingMetaMethodName, if no meta-method
|
35
|
+
# name was given.
|
36
|
+
# If a meta-method name is passed that already exists in the lookup table,
|
37
|
+
# a Seatbelt::Errors::MetaMethodDuplicateError is raised.
|
38
|
+
#
|
39
|
+
# This will be a private method with Seatbelt 1.0.
|
40
|
+
def api_method(*args)
|
41
|
+
raise Errors::ArgumentsMissmatchError if args.empty?
|
42
|
+
raise Errors::MissingMetaMethodName if args.first.is_a?(Hash)
|
43
|
+
if lookup_tbl.map{|n| n.keys}.flatten.include?(args.first)
|
44
|
+
raise Errors::MetaMethodDuplicateError
|
45
|
+
end
|
46
|
+
default_options = { :scope => :instance,
|
47
|
+
:block_required => false,
|
48
|
+
:arity => 0
|
49
|
+
}
|
50
|
+
options = args.extract_options!
|
51
|
+
if options.has_key?(:args)
|
52
|
+
arity = options.delete(:args)
|
53
|
+
size = arity.size
|
54
|
+
block_size = lambda do
|
55
|
+
block_match = arity.join(",").scan(/\&{1,}/)
|
56
|
+
size = size - block_match.size if block_match
|
57
|
+
return size
|
58
|
+
end
|
59
|
+
if arity.join(",").match(/\*{1,}/)
|
60
|
+
size = -block_size.call
|
61
|
+
eqls = arity.join(",").scan(/[\=]+/).size
|
62
|
+
size = size + eqls
|
63
|
+
default_options[:arity] = size
|
64
|
+
else
|
65
|
+
size = block_size.call
|
66
|
+
eqls = arity.join(",").scan(/[\=]+/).size
|
67
|
+
size = eqls > 0 ? -size : size
|
68
|
+
default_options[:arity] = size
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
meta_definitions = { args.first => default_options.merge(options) }
|
73
|
+
|
74
|
+
lookup_tbl.set(meta_definitions)
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# Public: An accessor to the lookup table array.
|
79
|
+
#
|
80
|
+
# Creates the table if it doesn't exists.
|
81
|
+
#
|
82
|
+
# Returns an instance of Lookup Table
|
83
|
+
def lookup_tbl
|
84
|
+
@lookup_tbl = LookupTable.new if @lookup_tbl.nil?
|
85
|
+
return @lookup_tbl
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|