rubysierung 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 038d493ea5841626de91eeb6f134e7718fb6acf6
4
- data.tar.gz: 1122c17da2a242e1cd6d8596ebe7f921e5b6feeb
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OTFkNDk1ZTEyYmJiZDZiMmFjNTFlMWY0OTE0MmFlY2RlOTA5YjY2YQ==
5
+ data.tar.gz: !binary |-
6
+ N2ZhODk2ZjA5MjM4NDVhMTJiNGZhODAxZDlhZjFmNGNiNTVlMjc0ZQ==
5
7
  SHA512:
6
- metadata.gz: 36b229c7d8aff827db09a889c841d125b97ffaa4566170cd1815502c9db22a8eee24abf8d9f30afa49888cd6bd9cca9d1ab9b0b88c66ab796ed976727c2c7eb2
7
- data.tar.gz: 83169296e36c106e58ea7bdb9f04828241db5baeb1686cafa6f7a3877fbdc09f90047766575970fd00ee7a53ec82654d46e690324a6b16ae641c01689af16f02
8
+ metadata.gz: !binary |-
9
+ MjcyMTMwODA2MmRmNzU0NzBlNzdlZjhmMTVkMDFiMTU2ZDYzOTc1NWFmZjIw
10
+ ZGNiNmMwNGNmNDhmODM5OTQ3ZmQ3ZTAwMWIzMmVmNDFjNDM0MGVlNDE2N2Yx
11
+ MDg3NzgxNzFkODljMmNhNzc2NzQ3NTYyMGEzZjI3NTA1MjRiOTA=
12
+ data.tar.gz: !binary |-
13
+ ZjcxYmMwMzJkMWZmZTdhMTdkMWU5YjM4NjAwYTM0OTkyNTkxYTAyMzU4NmI2
14
+ NzNhOTU1NGVhNTE1ZWJiYzAwZWU2ZTkxNGVmODFlNzBmMTYwODY3YTlkYTcz
15
+ YzFkNTIwOWQyMjVhZjNkMzA3MjEyODAzYWQxM2RhMzhkZjEwZGE=
data/.hound.yml ADDED
@@ -0,0 +1,3 @@
1
+ ruby:
2
+ enabled: true
3
+ config_file: .rubocop.yml
data/.rubocop.yml ADDED
File without changes
data/.travis.yml CHANGED
@@ -1,3 +1,12 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.0
3
+ - 2.1.0
4
+ deploy:
5
+ provider: rubygems
6
+ api_key:
7
+ secure: QA2OqytUOFV+W0dcIhULAS6bEZTMRfwrWHFmi1T4Ce3MtZ2o4f2kYsE+9wbFmfKqI35NljsUZYzunbAADGAXuGiir0pzFh5XzCJX/SPCUeNj79UwadByMmfI+dHzRTQdnPeGDsxBSg+N35yPCNenJlAgfX7Ed5YXJ4bAbWjPsds=
8
+ gem: rubysierung
9
+ on:
10
+ tags: true
11
+ all_branches: true
12
+ repo: doodzik/rubysierung
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # 0.0.3
2
+
3
+ - ADD: support of default values
4
+ - ADD: CallBaecker doesn't have to be included manually
5
+ - ADD: @__add_type 3rd argument is now optional and is set to the 2nd type
6
+ - FIX: strict custom_type
7
+ - REFACTOR: Tests seperated into different files
8
+ - REFACTOR: Source splitted into multiple types
data/README.md CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/doodzik/rubysierung.svg?branch=master)](https://travis-ci.org/doodzik/rubysierung)
4
4
 
5
- Rubysierung is the type system Ruby deserves
5
+ [![doodzik/rubysierung API Documentation](https://www.omniref.com/github/doodzik/rubysierung.png)](https://www.omniref.com/github/doodzik/rubysierung/HEAD)
6
+
7
+ Rubysierung is an implementation of Soft Typing in Ruby
8
+
6
9
 
7
10
  ## Installation
8
11
 
9
12
  Add this line to your application's Gemfile:
10
13
 
11
14
  ```ruby
12
- # ruby 2.1.0
13
-
15
+ # ruby >= 2.1.0
16
+ # You can only use Rubysierung in a file context, see issue #5 and #7.
14
17
  gem 'rubysierung'
15
- gem 'CallBaecker', '~> 0.0.3'
16
18
  ```
17
19
 
18
20
  And then execute:
@@ -25,25 +27,28 @@ Or install it yourself as:
25
27
 
26
28
  ## Usage
27
29
 
28
- ## Have a look at all available types in ./lib/rubysierung/types
30
+ ## Have a look at all available [Types] (https://github.com/doodzik/rubysierung/blob/master/lib/rubysierung/types.rb#L3-L19)
29
31
 
30
32
  ```ruby
31
- require 'CallBaecker'
32
33
  require 'rubysierung'
33
34
 
34
- # define a custom type
35
- class CustomType
36
- end
35
+ # define a custom types
36
+ class Strict::CustomTyp;end
37
+ class CustomTyp;end
38
+
39
+ class Strict::CustomTypX;end
40
+ class CustomTypX;end
37
41
 
38
42
  class Example
39
43
  extend Rubysierung
40
- include CallBaecker # include Callbaecker after Rubysierung
41
44
 
42
45
  # if the type doesnt match Rubisierung will raise an Error messages
43
46
 
44
47
  # add custom Types
45
- # [TypeClass, StandartDuckTypeAsSymbol, StrictDuckTypeAsSymbol]# TODO change to proper name
46
- @__add_type[CustomTyp, :to_s, :to_str]
48
+ # if you don't specify a Strict Type the standard type is being set for it
49
+ # [TypeClass, StandardConversionMethodAsSymbol, StrictConversionMethodSymbol]
50
+ @__add_type[CustomType, :to_s, :to_str]
51
+ @__add_type[CustomTypeX, :to_s]
47
52
 
48
53
  # define foo to respond to :to_s and bar to :to_i
49
54
  def one(foo: String, bar: Integer)
@@ -51,23 +56,28 @@ class Example
51
56
  end
52
57
 
53
58
  # you can still define empty/default parameters
54
- def self.three(foo: , bar: 'hallo World')
59
+ def self.two(foo:, bar: 'hallo World')
55
60
  [foo, bar]
56
61
  end
57
62
 
58
63
  # use a custom type
59
- def self.four(foo: ,bar: CustomType)
64
+ def self.three(foo:, bar: CustomType)
60
65
  [foo, bar]
61
66
  end
62
67
 
63
68
  # define foo to respond to :to_str (strict type)
64
- def self.five(foo: Strict::String, bar: Integer)
69
+ def self.four(foo: Strict::String, bar: Integer)
70
+ [foo, bar]
71
+ end
72
+
73
+ # with default parameters
74
+ def self.five(foo: String||'I am a default :)', bar: Integer||42)
65
75
  [foo, bar]
66
76
  end
67
77
  end
68
78
  ```
69
79
 
70
- ## what would it look like normally
80
+ ## what it would look like normally
71
81
  ```ruby
72
82
  def one(foo:, bar:)
73
83
  sFoo = foo.to_s
@@ -76,6 +86,10 @@ def one(foo:, bar:)
76
86
  end
77
87
  ```
78
88
 
89
+ ## Other Typing implementations
90
+ (contracts.ruby)[https://github.com/egonSchiele/contracts.ruby]
91
+ (typo)[https://github.com/hannestyden/typo/blob/master/typo.rb]
92
+
79
93
  ## Contributing
80
94
 
81
95
  1. Fork it ( https://github.com/doodzik/rubysierung/fork )
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  Rake::TestTask.new do |t|
5
- t.libs << 'test'
6
+ t.libs << 'test'
6
7
  end
7
8
 
8
- desc "Run tests"
9
- task :default => :test
9
+ RuboCop::RakeTask.new
10
10
 
11
+ desc 'Run tests and rubocop'
12
+ task default: [:test]
data/lib/ast/ast.rb ADDED
@@ -0,0 +1,32 @@
1
+ class AST < Struct.new(:to_parse)
2
+ # @return [Hash<String, String>] parameter of a method
3
+ def params
4
+ get_param_hash to_parse
5
+ end
6
+
7
+ # just so it makes sense in contest of the ast
8
+ # @return [self] make it chainable
9
+ def method
10
+ self
11
+ end
12
+
13
+ private
14
+
15
+ # @param declaration_string [String] the string which is being evaluated
16
+ # @return [Hash<String, String>] parameter of a method
17
+ def get_param_hash(declaration_string)
18
+ Hash[
19
+ *declaration_string.scan(regex_keyword_param).flatten
20
+ ].reject { |_, v| v =~ regex_isnt_constant }
21
+ end
22
+
23
+ # @return [Regex] which returns keywords and value from ruby string
24
+ def regex_keyword_param
25
+ /([a-z][a-zA-Z]+):\s*([^,\n)]+)/
26
+ end
27
+
28
+ # @return [Regex] which checks if the String isn't a Constant
29
+ def regex_isnt_constant
30
+ /^[^A-Z]/
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ require 'ast/ast'
2
+
3
+ module Rubysierung
4
+ class AST < AST
5
+ # Calls super and uses it result as a arg for default_param
6
+ # @return [Array<Hash<>, Hash<Symbol, String>>] returns the same as default_param
7
+ def params
8
+ default_param super
9
+ end
10
+
11
+ private
12
+
13
+ # @return [Regex] splits default param and type
14
+ def regex_default_arg
15
+ /([A-Z]\w*?)\s*\|\|\s*(.+)/
16
+ end
17
+
18
+ # seperates a param from default argument and stores the defaults inside a hash
19
+ # @param hash [Hash<String, String>] {"foo"=>"String||'bar'", "bar"=>"String||'foo'"}
20
+ # @return [Array<Hash<String, String>, Hash<Symbol, String>>] params, defaults for params
21
+ def default_param(hash)
22
+ defaults = {}
23
+ hash.map do |k, v|
24
+ const, default = v.scan(regex_default_arg).flatten
25
+ next unless const && default
26
+ hash[k], defaults[k.to_sym] = [const, default]
27
+ end
28
+ [hash, defaults]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,85 @@
1
+ require 'rubysierung/error'
2
+
3
+ module Rubysierung
4
+ module Core
5
+ # sets defaults as @__defaults
6
+ # @param ast [AST] an instance of modefied ruby ast class
7
+ # @todo refactor into adapter
8
+ # @return [Hash<Symbol, Constant>]
9
+ def default_hash(ast)
10
+ param_hash, defaults = ast.method.params
11
+ @__defaults = @__defaults.merge(defaults)
12
+ convert_value_to_constant param_hash
13
+ end
14
+
15
+ # applies conversion methods on args and returns args(for CallBaecker)
16
+ # @param klass_hash [Hash<Symbol, Constant>] holds types for Arguments
17
+ # @param value_hash [Hash<Symbol, Constant>] holds values for Arguments
18
+ # @return [Hash<Symbol, Constant>] same as value_hash only that on its
19
+ # values the type was applied and merged with the other values
20
+ def call(klass_hash:, value_hash:)
21
+ return value_hash if klass_hash.empty?
22
+ return_hash = each_param(klass_hash, value_hash) do |key|
23
+ @__error_data[:var_sym] = key
24
+ end
25
+ value_hash.merge return_hash
26
+ end
27
+
28
+ private
29
+
30
+ # @param klass_hash [Hash<Symbol, Constant>] holds types for Arguments
31
+ # @param value_hash [Hash<Symbol, Constant>] holds values for Arguments
32
+ # @yield [Symbol] param name
33
+ # @todo don't use eval -> String.new('default') something like this
34
+ # @todo refactor: i can't point my finger on it, but it feels bad
35
+ # @return [Hash] same as value_hash only that on its
36
+ # values the type was applied
37
+ def each_param(klass_hash, value_hash)
38
+ return_hash = {}
39
+ klass_hash.keys.map do |key|
40
+ yield(key) if block_given?
41
+ value = value_hash[key] ? value_hash[key] : eval(@__defaults[key])
42
+ return_hash[key] = convert(klass: klass_hash[key], value: value)
43
+ end
44
+ return_hash
45
+ end
46
+
47
+ # @param klass [Hash<>]
48
+ # @option value [Hash<>]
49
+ # @raise [Rubysierung::Error:Standard|Rubysierung::Error::Strict]
50
+ # @return [self]
51
+ def convert(klass:, value: nil)
52
+ strict, klass = get_kind(klass: klass)
53
+ type = @__types.reject { |t| klass != t[0] }.flatten
54
+ klass == type[0] ? value.send(type[1 + strict]) : value
55
+ rescue NoMethodError
56
+ err = @__error_data.merge(klass: klass, type: type[1 + strict],
57
+ value: value, value_class: value.class)
58
+ raise Rubysierung::Error.new(err).raise_child strict
59
+ end
60
+
61
+ # @param klass [String] takes the type of a klass
62
+ # @return [Array<Int, Constant>] if klass is Strict it returns 1 else 0 as
63
+ # the int. So that we can look up the conversion method in @__types
64
+ # and a klass without strict prefix
65
+ def get_kind(klass:)
66
+ if klass.respond_to? :name
67
+ splitted, splitted1 = klass.name.split('::')
68
+ splitted == 'Strict' ? [1, Kernel.const_get(splitted1)] : [0, klass]
69
+ else
70
+ [0, klass]
71
+ end
72
+ end
73
+
74
+ # @note this could be the point where we could use Symbols instead
75
+ # of Constants.
76
+ # @param hash [Hash<String, String>] takes a string string hash
77
+ # @todo refactor into adapter
78
+ # @return [Hash<Symbol, Constant>] returns a symbol Constant hash
79
+ def convert_value_to_constant(hash)
80
+ Hash[hash.map do |k, v|
81
+ [k.to_sym, Kernel.const_get(v)]
82
+ end]
83
+ end
84
+ end
85
+ end
@@ -1,4 +1,55 @@
1
- module Rubysierung::Error
2
- class Standard < StandardError; end
3
- class Strict < StandardError; end
1
+ module Rubysierung
2
+ class Error < StandardError
3
+ # @param error_data [Hash<..>] look at message what is needed
4
+ # @return [void]
5
+ def initialize(error_data)
6
+ @error_data = error_data
7
+ end
8
+
9
+ # @param strict [Int] 0 or 1, 1 if it strict
10
+ # @return [Rubysierung::Error::Standard|Rubysierung::Error::Strict] instance of one of the two classes
11
+ def raise_child(strict)
12
+ if strict == 0
13
+ Rubysierung::Error::Standard.new(@error_data)
14
+ else
15
+ Rubysierung::Error::Strict.new(@error_data)
16
+ end
17
+ end
18
+
19
+ # @todo refactor into idependend methods and assemble them inside message
20
+ # @return [String] error message
21
+ def message
22
+ "Class:#{@error_data[:klass]}, Conversion Method:#{@error_data[:type]}, Method:#{@error_data[:method_object]}:#{@error_data[:method_file]} #{@error_data[:method_name]}:#{@error_data[:method_line]} -- called on #{@error_data[:caller]} with #{@error_data[:var_sym]}:#{@error_data[:value]} of #{@error_data[:value_class]} doesn't respond to #{@error_data[:type]}"
23
+ end
24
+
25
+ # sets @__error_data on provided obj pointer
26
+ # @param _self [self]
27
+ # @param name [Symbol]
28
+ # @param method_object [?]
29
+ # @param file [String]
30
+ # @param line [Int]
31
+ # @return [void]
32
+ def self.set_data(_self:, name:, method_object:, file:, line:)
33
+ err_data = _self.instance_variable_get :@__error_data
34
+ err_data[:method_object] = method_object
35
+ err_data[:method_file] = file
36
+ err_data[:method_name] = name
37
+ err_data[:method_line] = line
38
+ _self.instance_variable_set :@__error_data, err_data
39
+ end
40
+ end
41
+
42
+ class Error::Standard < Error
43
+ # @return [String] Rubysierung::Error prefixed with self namespace
44
+ def message
45
+ "#{to_s}: #{super}"
46
+ end
47
+ end
48
+
49
+ class Error::Strict < Error
50
+ # @return [String] Rubysierung::Error prefixed with self namespace
51
+ def message
52
+ "#{to_s}: #{super}"
53
+ end
54
+ end
4
55
  end
@@ -9,11 +9,11 @@ module Rubysierung::Types
9
9
  [Float, :to_f, :to_f],
10
10
  [Hash, :to_h, :to_hash],
11
11
  [Rational, :to_r, :to_r],
12
- #[Enum, :to_enum, :to_enum],
12
+ # [Enum, :to_enum, :to_enum],
13
13
  [IO, :to_io, :to_io],
14
14
  [Proc, :to_proc, :to_proc],
15
- #[Path, :to_path, :to_path],
16
- #[JSON, :to_json, :to_json],
15
+ # [Path, :to_path, :to_path],
16
+ # [JSON, :to_json, :to_json],
17
17
  [Symbol, :to_sym, :to_sym],
18
18
  [Thread, :join, :join]
19
19
  ]
@@ -23,13 +23,13 @@ module Rubysierung::Types
23
23
  end
24
24
  end
25
25
 
26
- # Make Strict types accessible in Global namespace
26
+ # Make Strict types accessible in Global namespace
27
27
  module Strict
28
28
  end
29
29
 
30
- strictEval = ""
30
+ strict_eval = ''
31
31
 
32
- Rubysierung::Types.types.map { |types| strictEval += "class #{types[0]} ;end;" }
33
- Strict.module_eval strictEval
32
+ Rubysierung::Types.types.map { |typ| strict_eval += "class #{typ[0]} ;end;" }
33
+ Strict.module_eval strict_eval
34
34
 
35
- strictEval = nil
35
+ strict_eval = nil
@@ -1,3 +1,3 @@
1
1
  module Rubysierung
2
- VERSION = "0.0.2"
2
+ VERSION = '0.0.3'
3
3
  end
data/lib/rubysierung.rb CHANGED
@@ -1,92 +1,91 @@
1
- require "rubysierung/version"
1
+ require 'rubysierung/version'
2
2
  require 'rubysierung/types'
3
- require 'rubysierung/error'
3
+ require 'rubysierung/core'
4
+ require 'rubysierung/ast'
5
+ require 'CallBaecker'
4
6
 
7
+ # This is currently works as an Adapter for Rubysierung
8
+ # @todo refactor into its own adapter module
5
9
  module Rubysierung
6
- def self.extended(base)
7
- base.instance_variable_set :@__types_show, -> () do
8
- puts @__types
9
- end
10
-
11
- base.instance_variable_set :@__add_type, -> (klass, standard, strict) do
12
- @__types << [klass, standard, strict]
13
- end
14
-
15
- base.instance_variable_set :@__before_hook, -> (i,ii, callee) do
16
- @__error_data[:caller] = callee
17
- Rubysierung.convert_multiple(klass_hash: i, value_hash: ii)
18
- end
19
-
20
- base.instance_variable_set :@__setup_instance_method, -> (_self, name) do
21
- file, line = _self.instance_method(name.to_sym).source_location
22
- set_error_data(_self: self, name: name, method_object: _self.name, file: file, line: line - 1)
23
- get_default_hash_from_fileline(file: file, line: line-1)
24
- end
25
-
26
- base.instance_variable_set :@__setup_class_method, -> (_self, name) do
27
- file, line = _self.method(name.to_sym).source_location
28
- set_error_data(_self: self, name: name, method_object: _self.name, file: file, line: line - 1)
29
- get_default_hash_from_fileline(file: file, line: line-1)
30
- end
10
+ class << self
11
+ include Rubysierung::Core
31
12
  end
32
13
 
33
14
  @__types = Rubysierung::Types.types
15
+ # error data of the current method
34
16
  @__error_data = {}
35
- class << self
36
- def get_default_hash_from_fileline(file:, line:)
37
- params_matcher = /([a-z][a-zA-Z]+):\s*([^,\n)]+)/
38
- def_line = IO.readlines(file)[line]
39
- flatten_hash = Hash[*def_line.scan(params_matcher).flatten]
40
- myhash = flatten_hash.reject { |k,v| v =~ /^[^A-Z]/}
41
- Hash[myhash.map{|(k,v)| [k.to_sym, Kernel.const_get(v)]}]
42
- end
17
+ # default parameters of the current method
18
+ @__defaults = {}
19
+ @__method_data = {}
20
+
21
+ # @todo access __type through method and make it return the types not print
22
+ # @return [void] puts all current types
23
+ @__types_show = -> () do
24
+ puts @__types
25
+ end
43
26
 
44
- def convert_multiple(klass_hash:, value_hash:)
45
- return_hash = {}
46
- return value_hash if klass_hash.empty?
47
- klass_hash.keys.map do |key|
48
- @__error_data[:var_sym] = key
49
- return_hash[key] = convert(klass: klass_hash[key], value: value_hash[key])
50
- end
51
- value_hash.merge return_hash
27
+ # @param base [self] pointer to the class on which it is extended
28
+ # @todo get rid of this method in order to understand the source better
29
+ # @todo refactor into adapter
30
+ # @return [void]
31
+ def self.extended(base)
32
+ # @__before_hook is called by CallBaecker inside the defined method
33
+ # then Rubysierung is called with the klass_hash and value_hash
34
+ # @note This lambda is called before anything happens in a method
35
+ # @note This is the starting point of Rubysierung.
36
+ # @param klass_hash [Hash<Symbol, Constant>] holds types for Arguments
37
+ # @param value_hash [Hash<Symbol, Constant>] holds values for Arguments
38
+ # @param callee [String] first element of the current execution stack
39
+ # @param _self [self]
40
+ # @param name [String] name of method
41
+ # @see CallBaecker
42
+ # @return [self]
43
+ base.instance_variable_set :@__before_hook, -> (klass_hash, value_hash, callee, _self, name) do
44
+ @__error_data[:caller] = callee
45
+ Error.set_data @__method_data[name]
46
+ Rubysierung.call(klass_hash: klass_hash, value_hash: value_hash)
52
47
  end
53
48
 
54
- def convert(klass:, value:)
55
- strict = 0
56
- @__types.map do |type|
57
- strict, klass = get_kind(klass: klass)
58
- begin
59
- if klass == type[0]
60
- return value.send(type[1+strict])
61
- end
62
- rescue NoMethodError
63
- # return argument, param, duck_type, calle/receiver -> file, line
64
- if strict == 0
65
- raise Rubysierung::Error::Standart, "Rubysierung::Error::Standart: Class:#{klass}, DuckType:#{type[2]}, Method:#{@__error_data[:method_object]}:#{@__error_data[:method_file]}#{@__error_data[:method_name]}:#{@__error_data[:method_line]} -- called on #{@__error_data[:caller]} with #{@__error_data[:var_sym]}:#{value} of #{value.class} doesn't respond to #{type[2]}"
66
- else
67
- raise Rubysierung::Error::Strict, "Rubysierung::Error::Strict: Class:#{klass}, DuckType:#{type[2]}, Method:#{@__error_data[:method_object]}:#{@__error_data[:method_file]}#{@__error_data[:method_name]}:#{@__error_data[:method_line]} -- called on #{@__error_data[:caller]} with #{@__error_data[:var_sym]}:#{value} of #{value.class} doesn't respond to #{type[2]}"
68
- end
69
- end
70
- end
71
- value
49
+ # pushes an custom type onto @__types
50
+ # @param klass [Constant] the CustomType
51
+ # @param standard [Symbol] the standard conversion method
52
+ # @param strict [Symbol] the strict conversion method
53
+ # @todo access __type through method
54
+ # @return [self]
55
+ base.instance_variable_set :@__add_type, -> (klass, standard, strict = standard) do
56
+ @__types << [klass, standard, strict]
72
57
  end
73
58
 
74
- def get_kind(klass:)
75
- if klass.respond_to? :name
76
- splitted = klass.name.split('::')
77
- splitted[0] == 'Strict' ? [1, Kernel.const_get(splitted[1])] : [0, klass]
78
- else
79
- [0, klass]
80
- end
59
+ # @note This lambda is called before a method is defined inside method added
60
+ # @param _self [self] pointer to the implementation object
61
+ # @param name [Symbol] name of the method
62
+ # @see CallBaecker
63
+ # @todo get rid of :@__setup_instance_method/:@__setup_class_method
64
+ # and make it one
65
+ # @return [self]
66
+ base.instance_variable_set :@__setup_instance_method, -> (_self, name) do
67
+ file, line = _self.instance_method(name).source_location
68
+ ruby_str = IO.readlines(file)[line-1]
69
+ ast = Rubysierung::AST.new(ruby_str)
70
+ @__method_data[name] = {_self: self, name: name, method_object: _self.name, file: file, line: line}
71
+ default_hash(ast)
81
72
  end
82
73
 
83
- def set_error_data(_self:, name:, method_object:, file:, line:)
84
- err_data = _self.instance_variable_get :@__error_data
85
- err_data[:method_object] = method_object
86
- err_data[:method_file] = file
87
- err_data[:method_name] = name
88
- err_data[:method_line] = line
89
- _self.instance_variable_set :@__error_data, err_data
74
+ # @note This lambda is called before a method is defined inside method added
75
+ # @param _self [self] pointer to the implementation object
76
+ # @param name [Symbol] name of the method
77
+ # @todo get rid of :@__setup_instance_method/:@__setup_class_method
78
+ # and make it one
79
+ # @see CallBaecker
80
+ # @return [self]
81
+ base.instance_variable_set :@__setup_class_method, -> (_self, name) do
82
+ file, line = _self.method(name).source_location
83
+ ruby_str = IO.readlines(file)[line-1]
84
+ ast = Rubysierung::AST.new(ruby_str)
85
+ @__method_data[name] = {_self: self, name: name, method_object: _self.name, file: file, line: line}
86
+ default_hash(ast)
90
87
  end
88
+
89
+ base.include CallBaecker
91
90
  end
92
91
  end
data/rubysierung.gemspec CHANGED
@@ -4,21 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'rubysierung/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "rubysierung"
7
+ spec.name = 'rubysierung'
8
8
  spec.version = Rubysierung::VERSION
9
- spec.authors = ["doodzik"]
10
- spec.email = ["4004blog@gmail.com"]
11
- spec.summary = %q{Rubysierung is the type system Ruby deserves}
12
- spec.homepage = "https://github.com/doodzik/rubysierung"
13
- spec.license = "MIT"
9
+ spec.authors = ['doodzik']
10
+ spec.email = ['4004blog@gmail.com']
11
+ spec.summary = %q(Rubysierung is the type system Ruby deserves)
12
+ spec.homepage = 'https://github.com/doodzik/rubysierung'
13
+ spec.license = 'MIT'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
18
+ spec.require_paths = ['lib']
19
+ spec.required_ruby_version = '~> 2.1'
19
20
 
20
- spec.add_development_dependency "bundler", "~> 1.7"
21
- spec.add_development_dependency "rake", "~> 10.0"
22
- spec.add_development_dependency "CallBaecker", "> 0.0.3"
23
- spec.add_development_dependency "minitest"
21
+ spec.add_development_dependency 'bundler', '~> 1.6'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'minitest'
24
+ spec.add_development_dependency 'rubocop'
25
+
26
+ spec.add_dependency 'CallBaecker', '0.0.4'
24
27
  end
data/test/test_ast.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'minitest/autorun'
2
+ require 'ast/ast'
3
+
4
+ class AstTest < Minitest::Test
5
+ def test_params
6
+ ast = AST.new "def self.example2(foo: Strict::String, bar: Integer, buz: String||'fu')"
7
+ result = ast.send :params
8
+ assert_equal({"foo"=>"Strict::String", "bar"=>"Integer", "buz"=>"String||'fu'"}, result)
9
+ end
10
+
11
+ def test_method
12
+ ast = AST.new ''
13
+ assert_equal ast, ast.method
14
+ end
15
+
16
+ def test_regex_isnt_constant
17
+ ast = AST.new ''
18
+ regex = ast.send :regex_isnt_constant#/^[^A-Z]/
19
+ assert_equal(nil, regex =~ 'Foo')
20
+ assert_equal(0, regex =~ 'foo')
21
+ end
22
+
23
+ def test_regex_keyword_param
24
+ ast = AST.new ''
25
+ regex = ast.send :regex_keyword_param#/([a-z][a-zA-Z]+):\s*([^,\n)]+)/
26
+ str = "def self.example2(foo: Strict::String, bar: Integer, buz: String||'fu')"
27
+ assert_equal(str.scan(regex).flatten,
28
+ ['foo', 'Strict::String', 'bar', 'Integer', 'buz', "String||'fu'"])
29
+ end
30
+
31
+ def test_get_param_hash
32
+ # @param declaration_string [String] the string which is being evaluated
33
+ # @return [Hash<String, String>] parameter of a method
34
+ ast = AST.new ''
35
+ str = "def self.example2(foo: Strict::String, bar: Integer, buz: String||'fu')"
36
+ result = ast.send :get_param_hash, str
37
+ assert_equal({"foo"=>"Strict::String", "bar"=>"Integer", "buz"=>"String||'fu'"}, result)
38
+ end
39
+ end
data/test/test_core.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'minitest/autorun'
2
+ require 'rubysierung'
3
+
4
+ class RubysierungCoreTest < Minitest::Test
5
+ def test_rubisierung_converte_Int_to_string
6
+ assert_equal('23', Rubysierung.send(:convert, { klass: String, value: 23 }))
7
+ end
8
+ def test_rubisierung_converte_string_to_Int
9
+ assert_equal(23, Rubysierung.send(:convert, { klass: Integer, value: '23' }))
10
+ end
11
+
12
+ def test_rubisierung_converte_pass_nonexisting_klass_then_return_value
13
+ assert_equal('23', Rubysierung.send(:convert, { klass: 'huhu', value: '23' }))
14
+ end
15
+
16
+ def test_run_type_hash_agains_value_hash
17
+ assert_equal({ foo: 23, bar: '23' },
18
+ Rubysierung.call(value_hash: { foo: '23', bar: 23 },
19
+ klass_hash: { foo: Integer, bar: String }))
20
+ end
21
+ def test_run_type_hash_agains_value_hash_without_a_class_on_klass_hash
22
+ assert_equal({ foo: '23', bar: '23' },
23
+ Rubysierung.call(value_hash: { foo: '23', bar: 23 },
24
+ klass_hash: { foo: 14, bar: String }))
25
+ end
26
+
27
+ def test_is_strict
28
+ assert_equal([0, String], Rubysierung.send(:get_kind, { klass: String }))
29
+ assert_equal([1, String], Rubysierung.send(:get_kind, { klass: Strict::String }))
30
+ end
31
+ end
@@ -0,0 +1,45 @@
1
+ require 'minitest/autorun'
2
+ require 'rubysierung'
3
+
4
+ class SetupError
5
+ attr_reader :__error_data
6
+ def initialize
7
+ @__error_data = {}
8
+ end
9
+
10
+ def example
11
+ Rubysierung::Error.set_data(_self: self, name: 42, method_object: 42, file: 42, line: 42)
12
+ end
13
+ end
14
+
15
+ class RubysierungErrorTest < Minitest::Test
16
+ def test_Error
17
+ data = {}
18
+ [:klass, :type, :method_object, :method_file, :method_name, :method_line, :caller, :var_sym, :value, :value_class, :type].map { |sym| data[sym] = '42' }
19
+ raise Rubysierung::Error.new(data)
20
+ rescue Rubysierung::Error => e
21
+ assert_equal("Class:42, Conversion Method:42, Method:42:42 42:42 -- called on 42 with 42:42 of 42 doesn't respond to 42", e.message)
22
+ end
23
+
24
+ def test_Error_Standard
25
+ data = {}
26
+ [:klass, :type, :method_object, :method_file, :method_name, :method_line, :caller, :var_sym, :value, :value_class, :type].map { |sym| data[sym] = '42' }
27
+ raise Rubysierung::Error::Standard.new(data)
28
+ rescue Rubysierung::Error::Standard => e
29
+ assert_equal("Rubysierung::Error::Standard: Class:42, Conversion Method:42, Method:42:42 42:42 -- called on 42 with 42:42 of 42 doesn't respond to 42", e.message)
30
+ end
31
+
32
+ def test_Error_Strict
33
+ data = {}
34
+ [:klass, :type, :method_object, :method_file, :method_name, :method_line, :caller, :var_sym, :value, :value_class, :type].map { |sym| data[sym] = '42' }
35
+ fail Rubysierung::Error::Strict.new(data)
36
+ rescue Rubysierung::Error::Strict => e
37
+ assert_equal("Rubysierung::Error::Strict: Class:42, Conversion Method:42, Method:42:42 42:42 -- called on 42 with 42:42 of 42 doesn't respond to 42", e.message)
38
+ end
39
+
40
+ def test_set_data
41
+ s_e = SetupError.new
42
+ s_e.example
43
+ assert_equal({ method_object: 42, method_file: 42, method_name: 42, method_line: 42 }, s_e.__error_data)
44
+ end
45
+ end
@@ -1,15 +1,8 @@
1
1
  require 'minitest/autorun'
2
- require 'CallBaecker'
3
2
  require 'rubysierung'
4
3
 
5
- class CustomTyp
6
- end
7
-
8
- class Setup
4
+ class SetupRubysierung
9
5
  extend Rubysierung
10
- include CallBaecker
11
-
12
- @__add_type[CustomTyp, :to_s, :to_str]
13
6
 
14
7
  def example1(foo: String, bar: Integer)
15
8
  [foo, bar]
@@ -19,93 +12,52 @@ class Setup
19
12
  [foo, bar]
20
13
  end
21
14
 
22
- def self.example3(foo: , bar: 'hallo World')
23
- [foo, bar]
24
- end
25
-
26
- # custom Class
27
- def self.example4(foo: ,bar: CustomTyp)
15
+ def self.example3(foo:, bar: 'hallo World')
28
16
  [foo, bar]
29
17
  end
30
18
 
31
- # strict
32
- def self.example5(foo: Strict::String, bar: Integer)
19
+ # Default
20
+ def self.example4(foo: String||'bar', bar: String||'foo')
33
21
  [foo, bar]
34
22
  end
35
23
 
36
24
  # Default
37
- # def self.example6(foo: Default.new(String, 'bar'), bar: Default.new(String, 'foo'))
38
- # [foo, bar]
39
- # end
25
+ def self.example_err(foo: Strict::String, bar: String||'foo')
26
+ [foo, bar, (__LINE__ - 1), caller]
27
+ end
40
28
  end
41
29
 
42
30
  class RubysierungTest < Minitest::Test
43
- def test_rubisierung_converte_Int_to_string
44
- assert_equal('23', Rubysierung.convert(klass: String, value: 23))
45
- end
46
- def test_rubisierung_converte_string_to_Int
47
- assert_equal(23, Rubysierung.convert(klass: Integer, value: '23'))
48
- end
49
-
50
- def test_rubisierung_converte_pass_nonexisting_klass_then_return_value
51
- assert_equal('23', Rubysierung.convert(klass: 'huhu', value: '23'))
52
- end
53
-
54
- def test_run_type_hash_agains_value_hash
55
- assert_equal({foo: 23, bar:'23'},
56
- Rubysierung.convert_multiple(value_hash: {foo: '23', bar: 23},
57
- klass_hash: {foo: Integer, bar: String}))
58
- end
59
- def test_run_type_hash_agains_value_hash_without_a_class_on_klass_hash
60
- assert_equal({foo: '23', bar:'23'},
61
- Rubysierung.convert_multiple(value_hash: {foo: '23', bar: 23},
62
- klass_hash: {foo: 14, bar: String}))
63
- end
64
-
65
- def test_is_strict
66
- assert_equal([0, String], Rubysierung.get_kind(klass: String))
67
- assert_equal([1, String], Rubysierung.get_kind(klass: Strict::String))
31
+ def test_error_implementation
32
+ SetupRubysierung.example_err(foo: 4, bar: '3')
33
+ rescue Rubysierung::Error::Strict => e
34
+ foo, bar, line, callee = SetupRubysierung.example_err(foo: '4', bar: '3')
35
+ data = {klass: 'String', type: 'to_str', method_object: 'SetupRubysierung', method_file: __FILE__, method_name: 'example_err', method_line: line, caller: callee[4] , var_sym: 'foo', value: '4', value_class: 'Fixnum'}
36
+ str = Rubysierung::Error::Strict.new(data).message
37
+ assert_equal(str, e.message)
68
38
  end
69
39
 
70
40
  def test_example_2_standart
71
- foo, bar= Setup.example2(foo: 4, bar: '3')
41
+ foo, bar = SetupRubysierung.example2(foo: 4, bar: '3')
72
42
  assert_equal(3, bar)
73
43
  assert_equal('4', foo)
74
44
  end
75
45
 
76
46
  def test_example_1_instance_method
77
- foo, bar= Setup.new.example1(foo: 4, bar: '3')
47
+ foo, bar = SetupRubysierung.new.example1(foo: 4, bar: '3')
78
48
  assert_equal(3, bar)
79
49
  assert_equal('4', foo)
80
50
  end
81
51
 
82
52
  def test_example_3_default_works
83
- foo, bar= Setup.example3(foo: 4)
53
+ foo, bar = SetupRubysierung.example3(foo: 4)
84
54
  assert_equal('hallo World', bar)
85
55
  assert_equal(4, foo)
86
56
  end
87
57
 
88
- def test_example_4_custum_type
89
- foo, bar = Setup.example4(foo: 4, bar: 3)
90
- assert_equal('3', bar)
91
- assert_equal(4, foo)
58
+ def test_example_4_default_value
59
+ foo, bar = SetupRubysierung.example4(bar: 'buz')
60
+ assert_equal('buz', bar)
61
+ assert_equal('bar', foo)
92
62
  end
93
-
94
- def test_example_5_strict_type
95
- foo, bar = Setup.example5(foo: '4', bar: 2)
96
- assert_equal(2, bar)
97
- assert_equal('4', foo)
98
- begin
99
- Setup.example5(foo: 4, bar: 2)
100
- assert_equal(false, true)
101
- rescue StandardError => e
102
- assert_equal(true, true)
103
- end
104
- end
105
-
106
- # def test_example_6_default_value
107
- # foo, bar = Setup.example5(bar: 'bar')
108
- # assert_equal('bar', bar)
109
- # assert_equal('bar', foo)
110
- # end
111
63
  end
@@ -0,0 +1,25 @@
1
+ require 'minitest/autorun'
2
+ require 'rubysierung/ast'
3
+
4
+ class RubysierungAstTest < Minitest::Test
5
+ def test_params
6
+ ast = Rubysierung::AST.new "def self.example2(foo: Strict::String, bar: Integer, buz: String||'fu')"
7
+ param, default = ast.params
8
+ assert_equal({"foo"=>"Strict::String", "bar"=>"Integer", "buz"=>"String"}, param)
9
+ assert_equal({:buz=>"'fu'"}, default)
10
+ end
11
+
12
+ def test_regex_default_arg
13
+ ast = Rubysierung::AST.new ''
14
+ regex = ast.send :regex_default_arg
15
+ str = "String||'fu'"
16
+ assert_equal(str.scan(regex).flatten, ['String', "'fu'"])
17
+ end
18
+
19
+ def test_default_param
20
+ ast = Rubysierung::AST.new ''
21
+ param, default = ast.send :default_param, {"foo"=>"String||'bar'", "bar"=>"String||'foo'"}
22
+ assert_equal({'foo' => 'String', 'bar' => 'String'}, param)
23
+ assert_equal({foo: "'bar'", bar: "'foo'"}, default)
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ require 'minitest/autorun'
2
+ require 'rubysierung'
3
+
4
+ class Strict::CustomTyp;end
5
+ class CustomTyp;end
6
+
7
+
8
+ class SetupRubysierungTypes
9
+ extend Rubysierung
10
+
11
+ @__add_type[CustomTyp, :to_s, :to_str]
12
+
13
+ # custom Class
14
+ def self.example1(foo:, bar: CustomTyp)
15
+ [foo, bar]
16
+ end
17
+
18
+ # strict
19
+ def self.example2(foo: Strict::String, bar: Integer)
20
+ [foo, bar]
21
+ end
22
+
23
+ # Custom strict
24
+ def self.example3(foo: Strict::String, bar: Strict::CustomTyp)
25
+ [foo, bar]
26
+ end
27
+ end
28
+
29
+ class RubysierungTypesTest < Minitest::Test
30
+ def test_example_1_custum_type
31
+ foo, bar = SetupRubysierungTypes.example1(foo: 4, bar: 3)
32
+ assert_equal('3', bar)
33
+ assert_equal(4, foo)
34
+ end
35
+
36
+ def test_example_2_strict_type
37
+ foo, bar = SetupRubysierungTypes.example2(foo: '4', bar: 2)
38
+ assert_equal(2, bar)
39
+ assert_equal('4', foo)
40
+ SetupRubysierungTypes.example2(foo: 4, bar: 2)
41
+ assert_equal(false, true)
42
+ rescue StandardError
43
+ assert_equal(true, true)
44
+ end
45
+
46
+ def test_example_3_custum_type_strict
47
+ foo, bar = SetupRubysierungTypes.example3(foo: '4', bar: 'hi')
48
+ assert_equal('hi', bar)
49
+ assert_equal('4', foo)
50
+ end
51
+ end
metadata CHANGED
@@ -1,71 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubysierung
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - doodzik
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-16 00:00:00.000000000 Z
11
+ date: 2014-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '1.7'
19
+ version: '1.6'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '1.7'
26
+ version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: CallBaecker
42
+ name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">"
45
+ - - ! '>='
46
46
  - !ruby/object:Gem::Version
47
- version: 0.0.3
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">"
52
+ - - ! '>='
53
53
  - !ruby/object:Gem::Version
54
- version: 0.0.3
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: minitest
56
+ name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - ! '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: CallBaecker
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 0.0.4
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 0.0.4
69
83
  description:
70
84
  email:
71
85
  - 4004blog@gmail.com
@@ -73,18 +87,29 @@ executables: []
73
87
  extensions: []
74
88
  extra_rdoc_files: []
75
89
  files:
76
- - ".gitignore"
77
- - ".travis.yml"
90
+ - .gitignore
91
+ - .hound.yml
92
+ - .rubocop.yml
93
+ - .travis.yml
94
+ - CHANGELOG.md
78
95
  - Gemfile
79
96
  - LICENSE.txt
80
97
  - README.md
81
98
  - Rakefile
99
+ - lib/ast/ast.rb
82
100
  - lib/rubysierung.rb
101
+ - lib/rubysierung/ast.rb
102
+ - lib/rubysierung/core.rb
83
103
  - lib/rubysierung/error.rb
84
104
  - lib/rubysierung/types.rb
85
105
  - lib/rubysierung/version.rb
86
106
  - rubysierung.gemspec
107
+ - test/test_ast.rb
108
+ - test/test_core.rb
109
+ - test/test_error.rb
87
110
  - test/test_rubysierung.rb
111
+ - test/test_rubysierung_ast.rb
112
+ - test/test_types.rb
88
113
  homepage: https://github.com/doodzik/rubysierung
89
114
  licenses:
90
115
  - MIT
@@ -95,20 +120,24 @@ require_paths:
95
120
  - lib
96
121
  required_ruby_version: !ruby/object:Gem::Requirement
97
122
  requirements:
98
- - - ">="
123
+ - - ~>
99
124
  - !ruby/object:Gem::Version
100
- version: '0'
125
+ version: '2.1'
101
126
  required_rubygems_version: !ruby/object:Gem::Requirement
102
127
  requirements:
103
- - - ">="
128
+ - - ! '>='
104
129
  - !ruby/object:Gem::Version
105
130
  version: '0'
106
131
  requirements: []
107
132
  rubyforge_project:
108
- rubygems_version: 2.2.2
133
+ rubygems_version: 2.4.2
109
134
  signing_key:
110
135
  specification_version: 4
111
136
  summary: Rubysierung is the type system Ruby deserves
112
137
  test_files:
138
+ - test/test_ast.rb
139
+ - test/test_core.rb
140
+ - test/test_error.rb
113
141
  - test/test_rubysierung.rb
114
- has_rdoc:
142
+ - test/test_rubysierung_ast.rb
143
+ - test/test_types.rb