moguro 0.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/.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
|