jinterface 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/jinterface/arg_range.rb +38 -0
- data/lib/jinterface/interface_class_methods.rb +111 -0
- data/lib/jinterface.rb +82 -0
- data.tar.gz.sig +0 -0
- metadata +88 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
module JInterface
|
2
|
+
class ArgRange
|
3
|
+
attr :min, :max
|
4
|
+
def initialize(meth)
|
5
|
+
reset(meth)
|
6
|
+
end
|
7
|
+
def +(range)
|
8
|
+
if range.is_a? self.class
|
9
|
+
@min = range.min if range.min < @min
|
10
|
+
@max = range.max if range.max > @max
|
11
|
+
end
|
12
|
+
return self
|
13
|
+
end
|
14
|
+
def cover?(range)
|
15
|
+
return range.is_a?(self.class) &&
|
16
|
+
@min <= range.min && @max >= range.max
|
17
|
+
end
|
18
|
+
def reset(meth)
|
19
|
+
@min, @max = getMethodRange(meth)
|
20
|
+
end
|
21
|
+
def to_s()
|
22
|
+
"(#{@min}, #{@max}#{@max == Float::INFINITY ? ']' : ')'}"
|
23
|
+
end
|
24
|
+
private
|
25
|
+
def getMethodRange(meth)
|
26
|
+
return 0, 0 unless meth.is_a?(UnboundMethod) or meth.is_a?(Method)
|
27
|
+
#Get min and max number of args for method
|
28
|
+
min = 0
|
29
|
+
params = meth.parameters
|
30
|
+
max = params.length
|
31
|
+
params.each do |type, |
|
32
|
+
++min if type == :req
|
33
|
+
max = Float::INFINITY if type == :rest
|
34
|
+
end
|
35
|
+
return min, max
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'jinterface/interface_class_methods'
|
2
|
+
require 'jinterface/arg_range.rb'
|
3
|
+
module JInterface
|
4
|
+
module InterfaceClassMethods
|
5
|
+
def include(*args)
|
6
|
+
#For each inclusion
|
7
|
+
args.each do |mod|
|
8
|
+
#Make sure it is an interface
|
9
|
+
raise TypeError.new(
|
10
|
+
"Cannot include non-interface module"\
|
11
|
+
" '#{mod.name}' in interface '#{self.name}'."
|
12
|
+
) unless JInterface.interface? mod
|
13
|
+
#Add the method arrays together
|
14
|
+
arg_ranges = mod.instance_variable_get(:@arg_ranges)
|
15
|
+
arg_ranges.each do |method_name, range|
|
16
|
+
@arg_ranges[method_name] = range + @arg_ranges[method_name]
|
17
|
+
end
|
18
|
+
#Include it
|
19
|
+
super mod
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def included(obj)
|
23
|
+
#Ignore if included in an interface,
|
24
|
+
#include method will take care of checks
|
25
|
+
return if JInterface.interface?(obj)
|
26
|
+
#Cache public obj method names
|
27
|
+
obj_methods = Hash[obj.public_instance_methods.map.with_index.to_a]
|
28
|
+
#Check interface methods are implemented in obj
|
29
|
+
act_range = JInterface::ArgRange.new(nil)
|
30
|
+
self.public_instance_methods.each do |method_name|
|
31
|
+
#If method does not exist
|
32
|
+
if not obj_methods.key? method_name
|
33
|
+
#Raise error if under a different scope
|
34
|
+
[:private, :protected].each do |scope|
|
35
|
+
raise NameError.new(
|
36
|
+
"Method '#{method_name}' in class '#{obj.name}'"\
|
37
|
+
" must be declared as public, not #{scope},"\
|
38
|
+
" as declared in interface '#{self.name}'."
|
39
|
+
) if obj.send("#{scope}_method_defined?", method_name)
|
40
|
+
end
|
41
|
+
#If here, method does not exist. This is possible
|
42
|
+
#if the method was removed or made undefined
|
43
|
+
raise NotImplementedError.new(
|
44
|
+
"Method '#{method_name}' must be implemented in class"\
|
45
|
+
" '#{obj.name}' as declared in interface '#{self.name}'."
|
46
|
+
)
|
47
|
+
end
|
48
|
+
#Make sure method was overriden (implemented)
|
49
|
+
meth = obj.public_instance_method method_name
|
50
|
+
raise NotImplementedError.new(
|
51
|
+
"Method '#{method_name}' must be implemented in class"\
|
52
|
+
" '#{obj.name}' as declared in interface '#{meth.owner.name}'."
|
53
|
+
) if JInterface.interface? meth.owner
|
54
|
+
#Make sure method can handle number of args defined
|
55
|
+
act_range.reset(meth)
|
56
|
+
exp_range = @arg_ranges[method_name]
|
57
|
+
raise ArgumentError.new(
|
58
|
+
"Method '#{method_name}' in class '#{obj.name}'"\
|
59
|
+
" must handle #{exp_range} arguments, not #{act_range},"\
|
60
|
+
" as declared in included interface(s)."
|
61
|
+
) unless act_range.cover? exp_range
|
62
|
+
end
|
63
|
+
end
|
64
|
+
def method_added(method_name)
|
65
|
+
meth = self.public_instance_method(method_name)
|
66
|
+
@arg_ranges[method_name] = JInterface::ArgRange.new(meth) + @arg_ranges[method_name]
|
67
|
+
end
|
68
|
+
def private(*args)
|
69
|
+
raise SyntaxError.new(
|
70
|
+
"Interface '#{self.name}' cannot declare"\
|
71
|
+
" private methods, only public methods."
|
72
|
+
)
|
73
|
+
end
|
74
|
+
def protected(*args)
|
75
|
+
raise SyntaxError.new(
|
76
|
+
"Interface '#{self.name}' cannot declare"\
|
77
|
+
" protected methods, only public methods."
|
78
|
+
)
|
79
|
+
end
|
80
|
+
def singleton_method_added(*args)
|
81
|
+
raise SyntaxError.new(
|
82
|
+
"Interface '#{self.name}' cannot declare"\
|
83
|
+
" class methods, only instance methods."
|
84
|
+
)
|
85
|
+
end
|
86
|
+
def extend(*args)
|
87
|
+
raise SyntaxError.new(
|
88
|
+
"Interface '#{self.name}' cannot extend,"\
|
89
|
+
" only include other interfaces."
|
90
|
+
)
|
91
|
+
end
|
92
|
+
def extended(obj)
|
93
|
+
raise SyntaxError.new(
|
94
|
+
"Interface '#{self.name}' cannot be "\
|
95
|
+
" extended, only included, in '#{obj.name}'."
|
96
|
+
)
|
97
|
+
end
|
98
|
+
def prepend(*args)
|
99
|
+
raise SyntaxError.new(
|
100
|
+
"Interface '#{self.name}' cannot prepend,"\
|
101
|
+
" only include other interfaces."
|
102
|
+
)
|
103
|
+
end
|
104
|
+
def prepended(obj)
|
105
|
+
raise SyntaxError.new(
|
106
|
+
"Interface '#{self.name}' cannot be "\
|
107
|
+
" prepended, only included, in '#{obj.name}'."
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/jinterface.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'jinterface/interface_class_methods'
|
2
|
+
module JInterface
|
3
|
+
class << self
|
4
|
+
def included(mod)
|
5
|
+
#Do not do anything if in production
|
6
|
+
return if production?
|
7
|
+
#Make sure JInterface used with module
|
8
|
+
raise TypeError.new(
|
9
|
+
"JInterface cannot be used with class"\
|
10
|
+
" '#{mod.name}', only with modules."
|
11
|
+
) if mod.instance_of? Class
|
12
|
+
#Extend module
|
13
|
+
mod.class_eval do
|
14
|
+
extend JInterface::InterfaceClassMethods
|
15
|
+
@arg_ranges = {
|
16
|
+
#method_name => ArgRange
|
17
|
+
}
|
18
|
+
#In case JInterface inclusion was
|
19
|
+
#not first thing in class declaration:
|
20
|
+
#Raise error if private method defined
|
21
|
+
private_instance_methods.each do |method_name|
|
22
|
+
private method_name
|
23
|
+
end
|
24
|
+
#Raise error if protected method defined
|
25
|
+
protected_instance_methods.each do |method_name|
|
26
|
+
protected method_name
|
27
|
+
end
|
28
|
+
#Raise error if class method defined
|
29
|
+
methods(false).each do |method_name|
|
30
|
+
singleton_method_added method_name
|
31
|
+
end
|
32
|
+
#Raise error if non-interface included
|
33
|
+
included_modules.each do |mod|
|
34
|
+
include mod unless JInterface.interface? mod
|
35
|
+
end
|
36
|
+
#Raise error if module prepended
|
37
|
+
prepended_modules.each do |mod|
|
38
|
+
prepend mod
|
39
|
+
end if defined? prepended_modules
|
40
|
+
#Raise error if module extended
|
41
|
+
(
|
42
|
+
(class << mod; self; end).included_modules -
|
43
|
+
Module.included_modules -
|
44
|
+
[JInterface::InterfaceClassMethods]
|
45
|
+
).each do |mod|
|
46
|
+
extend mod
|
47
|
+
end
|
48
|
+
#Send each method through method_added method
|
49
|
+
public_instance_methods(false).each do |method_name|
|
50
|
+
method_added(method_name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
def extended(obj)
|
55
|
+
raise SyntaxError.new(
|
56
|
+
"Interface '#{self.name}' cannot be "\
|
57
|
+
" extended, only included, in '#{obj.name}'."
|
58
|
+
)
|
59
|
+
end
|
60
|
+
def prepended(obj)
|
61
|
+
raise SyntaxError.new(
|
62
|
+
"Interface '#{self.name}' cannot be "\
|
63
|
+
" prepended, only included, in '#{obj.name}'."
|
64
|
+
)
|
65
|
+
end
|
66
|
+
def interface?(obj)
|
67
|
+
( obj == JInterface or
|
68
|
+
( obj.instance_of?(Module) and
|
69
|
+
(class << obj; self end) < JInterface::InterfaceClassMethods
|
70
|
+
)
|
71
|
+
) ? true : false
|
72
|
+
end
|
73
|
+
def production=(flag)
|
74
|
+
@enabled = flag == true
|
75
|
+
end
|
76
|
+
def production?()
|
77
|
+
return @enabled ||= false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
#Turn JInterface checking on or off
|
82
|
+
JInterface.production = defined?(Rails) and Rails.is_a?(Class) ? Rails.env.production? : false
|
data.tar.gz.sig
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jinterface
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 31
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 0.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Michael Rojas
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain:
|
17
|
+
- |
|
18
|
+
-----BEGIN CERTIFICATE-----
|
19
|
+
MIIDRDCCAiygAwIBAgIBADANBgkqhkiG9w0BAQUFADBIMRowGAYDVQQDDBFkZXYu
|
20
|
+
bWljaGFlbC5yb2phczEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
|
21
|
+
LGQBGRYDY29tMB4XDTE0MDQxMTIyMzM1M1oXDTE1MDQxMTIyMzM1M1owSDEaMBgG
|
22
|
+
A1UEAwwRZGV2Lm1pY2hhZWwucm9qYXMxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDET
|
23
|
+
MBEGCgmSJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
24
|
+
ggEBAKqba7uCWi7wkNCeUFLDa+Ql5fG75E8kGruxP5BBAWhOUwjWpKfUv/8Paqlk
|
25
|
+
7P3rtNMLnofentNQciXLgSlK0jwyO9Kuevi3nIPbFuVcQR5D58z9SLezYQSl1hPu
|
26
|
+
RaO2IFLfnakj2QjyH3RVdzBDq3+merqkI0qmDvWCs6aJljHsAOBjk5DftY5elCDP
|
27
|
+
ooks39ZVpsdwpoAewmtq9uvMivCRz+Vf1yJYVoRuVUSczoOPgN4PW/MJfLQhih5b
|
28
|
+
FrsDqGQdNgSjgL7rTZMd0/tiUdo9GaGYG/Rl3TtzlyxB9pJTTc1rDmi/1A2G6Weo
|
29
|
+
UX97pHPgP+0WEl3e41jbYkClSfMCAwEAAaM5MDcwHQYDVR0OBBYEFDWsnI/cwObC
|
30
|
+
XU3Yf8T0vyKp9Wb+MAsGA1UdDwQEAwIEsDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEB
|
31
|
+
BQUAA4IBAQAcCK9Sfoae8T+Sw7xY5ZMUAt55IO8Ldq61J+V8+Q49vgXb7/m379DC
|
32
|
+
FZhia4pj4n1g//M0nHm2/yQblXK7qwkYpGB3uEK5yedaY5fCGZlRe67rcPJ4No1x
|
33
|
+
tHpBaYknGBP3ITEt1Btw5HhPpccirBk8cBIP9vEN/k9yNn9jpVRRBLXiXaKusDRy
|
34
|
+
NccuJexQuG86gK8tnQh1kN2t+MA5UkDeHWsysJrEsT1/p6UChw60MhlQ2gFTrIvu
|
35
|
+
EBm0ICzl7th3TQgzeiWbMUiUx7k5B+Oi/btCNjCIGkpWyWC+nL1SweWtOFGQGuGm
|
36
|
+
UX363H4LFf3sXXUY2b+Nf5+zMlVQADoL
|
37
|
+
-----END CERTIFICATE-----
|
38
|
+
|
39
|
+
date: 2014-04-09 00:00:00 Z
|
40
|
+
dependencies: []
|
41
|
+
|
42
|
+
description: Simulate java interfaces in ruby to enforce public class implementation
|
43
|
+
email: dev.michael.rojas@gmail.com
|
44
|
+
executables: []
|
45
|
+
|
46
|
+
extensions: []
|
47
|
+
|
48
|
+
extra_rdoc_files: []
|
49
|
+
|
50
|
+
files:
|
51
|
+
- lib/jinterface.rb
|
52
|
+
- lib/jinterface/arg_range.rb
|
53
|
+
- lib/jinterface/interface_class_methods.rb
|
54
|
+
homepage: https://github.com/havelessbemore/JInterface
|
55
|
+
licenses:
|
56
|
+
- MIT
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
hash: 3
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.8.24
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: JInterface
|
87
|
+
test_files: []
|
88
|
+
|
metadata.gz.sig
ADDED
Binary file
|