jinterface 0.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.
- 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
|