moguro 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +48 -0
- data/.travis.yml +9 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/Rakefile +12 -0
- data/example.rb +64 -0
- data/lib/moguro.rb +33 -0
- data/lib/moguro/caluse.rb +90 -0
- data/lib/moguro/contract.rb +67 -0
- data/lib/moguro/decorator.rb +69 -0
- data/lib/moguro/errors.rb +97 -0
- data/lib/moguro/extractor.rb +9 -0
- data/lib/moguro/extractors/arguments_extractor.rb +36 -0
- data/lib/moguro/extractors/return_value_extractor.rb +19 -0
- data/lib/moguro/handler.rb +9 -0
- data/lib/moguro/handlers/class_handler.rb +71 -0
- data/lib/moguro/handlers/method_handler.rb +74 -0
- data/lib/moguro/method_reference.rb +45 -0
- data/lib/moguro/processors/arguments_processor.rb +42 -0
- data/lib/moguro/processors/contract_processor.rb +66 -0
- data/lib/moguro/processors/enumerable_processor.rb +43 -0
- data/lib/moguro/processros.rb +10 -0
- data/lib/moguro/sandbox.rb +15 -0
- data/lib/moguro/types.rb +37 -0
- data/lib/moguro/types/any.rb +23 -0
- data/lib/moguro/types/boolean.rb +29 -0
- data/lib/moguro/types/enumerable.rb +47 -0
- data/lib/moguro/types/nil.rb +18 -0
- data/lib/moguro/types/skin.rb +29 -0
- data/lib/moguro/values.rb +72 -0
- data/lib/moguro/version.rb +5 -0
- data/moguro.gemspec +37 -0
- metadata +263 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
# MethodReference represents original method reference that was
|
5
|
+
# decorated by contracts.ruby. Used for instance methods.
|
6
|
+
# This class borrowed many source code from https://github.com/egonSchiele/contracts.ruby
|
7
|
+
# License:: https://github.com/egonSchiele/contracts.ruby/blob/master/LICENSE
|
8
|
+
class MethodReference
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# name - name of the method
|
12
|
+
# method - method object
|
13
|
+
def initialize(method)
|
14
|
+
@name = method.name
|
15
|
+
@method = method
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns method_position, delegates to Support.method_position
|
19
|
+
def position
|
20
|
+
file, line = @method.source_location
|
21
|
+
if file.nil? || line.nil?
|
22
|
+
''
|
23
|
+
else
|
24
|
+
"#{file}:#{line}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def parameters
|
29
|
+
@method.parameters
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# The same as MethodReference, but used for singleton methods.
|
34
|
+
class SingletonMethodReference < MethodReference
|
35
|
+
private
|
36
|
+
|
37
|
+
def private?(this)
|
38
|
+
this.private_methods.map(&:to_sym).include?(name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def protected?(this)
|
42
|
+
this.protected_methods.map(&:to_sym).include?(name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
module Processors
|
5
|
+
class ArgumentsProcessor < ::Parser::AST::Processor
|
6
|
+
attr_reader :rule
|
7
|
+
|
8
|
+
def initialize(klass, clause)
|
9
|
+
@klass = klass
|
10
|
+
@clause = clause
|
11
|
+
super()
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_const(node)
|
15
|
+
symbol = node.children.last
|
16
|
+
validator = Moguro::Types.get_type_validator_from_const(get_const(symbol))
|
17
|
+
@array_processor = nil
|
18
|
+
@clause.add_type(validator)
|
19
|
+
super node
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_nil(_node)
|
23
|
+
validator = Moguro::Types.get_type_validator_from_const(nil)
|
24
|
+
@clause.add_type(validator)
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
return unless node.children[1] == :[]
|
29
|
+
@clause.pop_type
|
30
|
+
@clause.add_type(
|
31
|
+
Moguro::Processors::EnumerableProcessor.generate_type_validator(node, @klass)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def get_const(symbol)
|
38
|
+
@klass.const_get(symbol)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
module Processors
|
5
|
+
class ContractParser
|
6
|
+
def initialize(str)
|
7
|
+
@str = str
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse
|
11
|
+
::Parser::CurrentRuby.parse(@str)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ContractProcessor < ::Parser::AST::Processor
|
16
|
+
attr_reader :clauses
|
17
|
+
Rule = Struct.new(:key, :clauses)
|
18
|
+
|
19
|
+
def initialize(klass)
|
20
|
+
@current_arg = nil
|
21
|
+
@klass = klass
|
22
|
+
@clauses = Clauses.new
|
23
|
+
super()
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_kwarg(node)
|
27
|
+
process_args(node)
|
28
|
+
super node
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_kwoptarg(node)
|
32
|
+
process_args(node)
|
33
|
+
super node
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
def generate_type_validator(function, klass)
|
38
|
+
ast = ContractParser.new(
|
39
|
+
function.source
|
40
|
+
).parse
|
41
|
+
|
42
|
+
processor = ContractProcessor.new(klass)
|
43
|
+
processor.process(ast)
|
44
|
+
processor.clauses
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def process_args(node)
|
51
|
+
argument_key, _other = *node
|
52
|
+
rotate_rule(argument_key)
|
53
|
+
processor = ArgumentsProcessor.new(@klass, current_clause)
|
54
|
+
processor.process(node)
|
55
|
+
end
|
56
|
+
|
57
|
+
def rotate_rule(argument_key)
|
58
|
+
@clauses.add_verified_argument(argument_key)
|
59
|
+
end
|
60
|
+
|
61
|
+
def current_clause
|
62
|
+
clauses.last
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
module Processors
|
5
|
+
class EnumerableProcessor < ::Parser::AST::Processor
|
6
|
+
attr_reader :enumerate_validator
|
7
|
+
|
8
|
+
def initialize(klass)
|
9
|
+
@current_arg = nil
|
10
|
+
@klass = klass
|
11
|
+
@enumerate_validator = nil
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_const(node)
|
16
|
+
symbol = node.children.last
|
17
|
+
type = @klass.const_get(symbol)
|
18
|
+
if @enumerate_validator.nil?
|
19
|
+
@enumerate_validator ||= Moguro::Types::Enumerable.new(type)
|
20
|
+
else
|
21
|
+
validator = Moguro::Types.get_type_validator_from_const(type)
|
22
|
+
@enumerate_validator.add_content_type(validator)
|
23
|
+
end
|
24
|
+
super node
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
@base_type = false if node.children[1] == :[]
|
29
|
+
super node
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def generate_type_validator(ast, klass)
|
36
|
+
processor = EnumerableProcessor.new(klass)
|
37
|
+
processor.process(ast)
|
38
|
+
processor.enumerate_validator
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
class SandBox
|
5
|
+
include Test::Unit::Assertions
|
6
|
+
|
7
|
+
def initialize(klass)
|
8
|
+
@klass = klass
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method_name, *args, &blk)
|
12
|
+
@klass.__send__(method_name, *args, &blk)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/moguro/types.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
module Types
|
5
|
+
Result = Struct.new(:emumerable_validator, :valid?)
|
6
|
+
class << self
|
7
|
+
def get_type_validator_from_const(type)
|
8
|
+
if type == Moguro::Types::Boolean
|
9
|
+
type.new
|
10
|
+
elsif type.nil?
|
11
|
+
Nil.new
|
12
|
+
elsif type == ::Array || type.include?(::Enumerable)
|
13
|
+
Enumerable.new(type)
|
14
|
+
else
|
15
|
+
Skin.new(type)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
###
|
21
|
+
# Type match class
|
22
|
+
# @private
|
23
|
+
# @since 0.0.1
|
24
|
+
# @abstract
|
25
|
+
class Base
|
26
|
+
def valid?(_p)
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
require_relative 'types/skin'
|
34
|
+
require_relative 'types/enumerable'
|
35
|
+
require_relative 'types/boolean'
|
36
|
+
require_relative 'types/nil'
|
37
|
+
require_relative 'types/any'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
module Types
|
5
|
+
###
|
6
|
+
#
|
7
|
+
# @since 0.0.1
|
8
|
+
class Any < Base
|
9
|
+
def valid?(_val)
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
###
|
14
|
+
# Type name for error message
|
15
|
+
# @since 0.0.1
|
16
|
+
# @private
|
17
|
+
###
|
18
|
+
def type
|
19
|
+
'Any'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
module Types
|
5
|
+
###
|
6
|
+
# Fake Boolean class for type definition
|
7
|
+
# @since 0.0.1
|
8
|
+
###
|
9
|
+
class Boolean < Base
|
10
|
+
###
|
11
|
+
# validate value type
|
12
|
+
# TrueClass or FalseClass
|
13
|
+
# @since 0.0.1
|
14
|
+
###
|
15
|
+
def valid?(val)
|
16
|
+
val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
17
|
+
end
|
18
|
+
|
19
|
+
###
|
20
|
+
# Type name for error message
|
21
|
+
# @since 0.0.1
|
22
|
+
# @private
|
23
|
+
###
|
24
|
+
def type
|
25
|
+
'TrueClass or FalseClass'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
module Types
|
5
|
+
###
|
6
|
+
# Array type validator
|
7
|
+
# @since 0.0.1
|
8
|
+
# @private
|
9
|
+
###
|
10
|
+
class Enumerable < Base
|
11
|
+
def initialize(klass)
|
12
|
+
@content_type = []
|
13
|
+
@klass = klass
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_content_type(type)
|
17
|
+
@content_type << type
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?(val)
|
21
|
+
eumerable?(val) && (val.empty? || content_type_valid?(val))
|
22
|
+
end
|
23
|
+
|
24
|
+
def type
|
25
|
+
if @content_type.empty?
|
26
|
+
@klass.to_s
|
27
|
+
else
|
28
|
+
"#{@klass}<#{@content_type.join('|')}>"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def eumerable?(val)
|
35
|
+
val.is_a?(@klass)
|
36
|
+
end
|
37
|
+
|
38
|
+
def content_type_valid?(val)
|
39
|
+
return true if @content_type.empty?
|
40
|
+
|
41
|
+
val.all? do |v|
|
42
|
+
@content_type.any? { |type| type.valid?(v) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
module Types
|
5
|
+
###
|
6
|
+
# The thin wrapper class for some instance object
|
7
|
+
# @since 0.0.1
|
8
|
+
###
|
9
|
+
class Skin < SimpleDelegator
|
10
|
+
def initialize(klass)
|
11
|
+
@klass = klass
|
12
|
+
super(@klass)
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?(val)
|
16
|
+
val.is_a?(@klass)
|
17
|
+
end
|
18
|
+
|
19
|
+
###
|
20
|
+
# Class name for error message
|
21
|
+
# @since 0.0.1
|
22
|
+
# @private
|
23
|
+
###
|
24
|
+
def type
|
25
|
+
@klass
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moguro
|
4
|
+
###
|
5
|
+
# Wrapper class for Arguments
|
6
|
+
# @since 0.0.1
|
7
|
+
# @private
|
8
|
+
###
|
9
|
+
class Values < DelegateClass(Array)
|
10
|
+
def initialize
|
11
|
+
super([])
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_h
|
15
|
+
each_with_object({}) do |value_class, hash|
|
16
|
+
hash[value_class.key] = value_class.value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_value(key, value, missing: false)
|
21
|
+
self << Value.new(key, value, missing: missing)
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
map(&:inspect)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
###
|
30
|
+
# Wrapper class for Arguments value
|
31
|
+
# @since 0.0.1
|
32
|
+
# @private
|
33
|
+
###
|
34
|
+
class Value < SimpleDelegator
|
35
|
+
attr_reader :key, :value
|
36
|
+
|
37
|
+
def initialize(key, value, missing: false)
|
38
|
+
@key = key
|
39
|
+
@value = value
|
40
|
+
@missing = missing
|
41
|
+
super(@value)
|
42
|
+
end
|
43
|
+
|
44
|
+
def missing?
|
45
|
+
@missing
|
46
|
+
end
|
47
|
+
|
48
|
+
def class
|
49
|
+
@value.class
|
50
|
+
end
|
51
|
+
|
52
|
+
def is_a?(val)
|
53
|
+
@value.is_a?(val)
|
54
|
+
end
|
55
|
+
|
56
|
+
def nil?
|
57
|
+
@value.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
"#{key}: #{type_inspect}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def type_inspect
|
65
|
+
if @value.class.include?(Enumerable) && !@value.empty?
|
66
|
+
"#{value}(#{value.class}<#{@value.map(&:class).uniq.join('|')}>)"
|
67
|
+
else
|
68
|
+
"#{value}(#{value.class})"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|