typedeaf 0.0.1 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3a8ff4243653190eb9017aabc61f79b3654f4dd2
4
- data.tar.gz: 7d7c83e7048c3667c48096ebf812a311c5c375fb
3
+ metadata.gz: d9fea842c38105ee58ed6274182e98d48af63414
4
+ data.tar.gz: 1174982311c6dc067f43186110909fc3b8f9b2be
5
5
  SHA512:
6
- metadata.gz: a454e734801b2f24eef4d02d2e81d9f46c6b8aacb20ee6f2aad1a85056bcf98647e7375c8d64492c69d4a9dd64c49cfa98b29967b81dec0aa9f07160894fce54
7
- data.tar.gz: 92f730e8823c3c7fe8900b051f6277785bac51b9fbf5aa816ce48bd5fe2fc08b03ae82360fb9ea86bcfaa99a10beb1207a6b1e32e4fd244de32147188c55627b
6
+ metadata.gz: 4cdb9589f43469a270fdea4efea6066ce48c4b95376abd16530b6e87851d7647da16bf6df452c8a24ade90186899c832be56a49035c99845cf5c96f1ae502ac5
7
+ data.tar.gz: ad794a1c79c07883854250e988f12f6b5901cc364ca85151fb3934fab0bac496d131cc61a15501e1f9453bb4bcb20250f31af0c6edc8a0db7d62348fb649cec0
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - jruby-19mode
4
+ - rbx-2
5
+ - ruby-head
6
+ - jruby-head
7
+ - 1.9.3
8
+
data/Gemfile CHANGED
@@ -6,6 +6,8 @@ gemspec
6
6
  group :development do
7
7
  gem 'rake'
8
8
  gem 'rspec'
9
+ gem 'rspec-its'
9
10
  gem 'pry'
10
- gem 'debugger-pry', :platform => :mri
11
+ gem 'debugger-pry', :platform => :mri_19
12
+ gem 'benchmark-ips'
11
13
  end
data/README.md CHANGED
@@ -1,35 +1,94 @@
1
1
  # Typedeaf
2
2
 
3
+ [![Build Status](https://travis-ci.org/rtyler/typedeaf.svg)](https://travis-ci.org/rtyler/typedeaf)
4
+
3
5
  Typedeaf is a gem to help add some type-checking to method declarations in Ruby
4
6
 
5
7
 
8
+ [RDoc](http://www.rubydoc.info/github/rtyler/typedeaf/master/frames)
9
+
10
+
6
11
  ## Usage
7
12
 
13
+ ### Writing your Typedeaf'd code
14
+
15
+ ```ruby
16
+ require 'typedeaf'
17
+
18
+ class Logger
19
+ include Typedeaf
20
+
21
+ # Log an error
22
+ #
23
+ # @param [String] messaage
24
+ define :error, message: String do
25
+ # Just delegate to the #log method nad pass
26
+ # the level of error
27
+ self.log(message, :error)
28
+ end
29
+
30
+ # Log a simple message to STDOUT
31
+ #
32
+ # @param [String] message The log message to log
33
+ # @param [Symbol] level The level at which to log the
34
+ # message, defaults to :info
35
+ define :log, message: String, level: default(:info, Symbol) do
36
+ puts "[#{level}] #{message}"
37
+ end
38
+ end
39
+ ```
40
+
41
+ ### Calling Typedeaf'd code
42
+
8
43
  ```
9
- [1] pry(main)> require 'typedeaf'
44
+ [1] pry(main)> require './logger'
10
45
  => true
11
- [2] pry(main)> class Logger
12
- [2] pry(main)* include Typedeaf
13
- [2] pry(main)*
14
- [2] pry(main)* define :log, message: String, level: Symbol do
15
- [2] pry(main)* puts "Logging #{message} at level #{level}"
16
- [2] pry(main)* end
17
- [2] pry(main)* end
18
- => Logger
19
- [3] pry(main)> l = Logger.new
46
+ [2] pry(main)> l = Logger.new
20
47
  => #<Logger:0x00000803c616b8>
21
- [4] pry(main)> l.log("Hello World", :debug)
22
- Logging Hello World at level debug
48
+ [3] pry(main)> l.log 'test 1, 2, 3'
49
+ [info] test 1, 2, 3
50
+ => nil
51
+ [4] pry(main)> l.log 'this is SUPER SERIOUS', :critical
52
+ [critical] this is SUPER SERIOUS
23
53
  => nil
24
- [5] pry(main)> l.log(4, :debug)
54
+ [5] pry(main)> l.error(5) # wrong type!
25
55
  Typedeaf::InvalidTypeException: Expected `message` to be a kind of String but was Fixnum
26
- from /usr/home/tyler/source/github/ruby/typedeaf/lib/typedeaf.rb:41:in `type_validation!'
27
- [6] pry(main)> l.log('Whoopsies', 'debug')
28
- Typedeaf::InvalidTypeException: Expected `level` to be a kind of Symbol but was String
29
- from /usr/home/tyler/source/github/ruby/typedeaf/lib/typedeaf.rb:41:in `type_validation!'
56
+ from /usr/home/tyler/source/github/ruby/typedeaf/lib/typedeaf.rb:58:in `type_validation!'
57
+ [6] pry(main)> l.log("This doesn't use the right type either", 1)
58
+ Typedeaf::InvalidTypeException: Expected `level` to be a kind of [Symbol] but was Fixnum
59
+ from /usr/home/tyler/source/github/ruby/typedeaf/lib/typedeaf.rb:58:in `type_validation!'
30
60
  [7] pry(main)>
61
+
31
62
  ```
32
63
 
64
+ #### Passing blocks to Typedeaf code
65
+
66
+ ```ruby
67
+ require 'typedeaf'
68
+
69
+ class Logger
70
+ include Typedeaf
71
+
72
+ define :log, message: String, block: Proc do
73
+ puts "Loggin: #{message}"
74
+ block.call
75
+ end
76
+ end
77
+
78
+ Logger.new.log('hello world') do
79
+ puts 'called back!'
80
+ end
81
+ ```
82
+
83
+ ## Caveats
84
+
85
+ Here's a list of caveats, or things that don't quite work, with Typedeaf:
86
+
87
+ * Typedeaf methods cannot use `return` or `break`, since they're technically
88
+ Ruby blocks and must follow the rules defined for block behaviors
89
+ * `yield` will not properly work, if you want to pass blocks into your
90
+ Typedeafed method, see the above usage example
91
+
33
92
 
34
93
  ## Installation
35
94
 
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
4
+ require 'typedeaf'
5
+ require 'benchmark/ips'
6
+
@@ -0,0 +1,27 @@
1
+ require_relative 'bench_helper'
2
+
3
+ class MissingArgs
4
+ include Typedeaf
5
+
6
+ def method_with_params(buffer)
7
+ buffer.size + rand + rand
8
+ end
9
+
10
+ define :typedeaf_with_params, buffer: String do
11
+ buffer.size + rand + rand
12
+ end
13
+ end
14
+
15
+ blk = lambda do |buffer|
16
+ buffer.size + rand + rand
17
+ end
18
+
19
+ p = MissingArgs.new
20
+
21
+
22
+ Benchmark.ips do |x|
23
+ x.report('typedeaf method') { begin; p.typedeaf_with_params; rescue; end; }
24
+ x.report('normal method') { begin; p.method_with_params; rescue; end; }
25
+ x.report('a simple proc') { begin; blk.(); rescue; end; }
26
+ x.compare!
27
+ end
@@ -0,0 +1,15 @@
1
+ As of: 790130d
2
+
3
+ Calculating -------------------------------------
4
+ typedeaf method 2292 i/100ms
5
+ normal method 2889 i/100ms
6
+ a simple proc 2594 i/100ms
7
+ -------------------------------------------------
8
+ typedeaf method 27691.2 (±12.6%) i/s - 137520 in 5.057106s
9
+ normal method 34595.8 (±12.9%) i/s - 170451 in 5.028954s
10
+ a simple proc 31650.1 (±15.4%) i/s - 155640 in 5.058063s
11
+
12
+ Comparison:
13
+ normal method: 34595.8 i/s
14
+ a simple proc: 31650.1 i/s - 1.09x slower
15
+ typedeaf method: 27691.2 i/s - 1.25x slower
@@ -0,0 +1,26 @@
1
+ require_relative 'bench_helper'
2
+
3
+ class Parametered
4
+ include Typedeaf
5
+
6
+ def method_with_params(buffer)
7
+ buffer.size + rand + rand
8
+ end
9
+
10
+ define :typedeaf_with_params, buffer: String do
11
+ buffer.size + rand + rand
12
+ end
13
+ end
14
+
15
+ blk = Proc.new do |buffer|
16
+ buffer.size + rand + rand
17
+ end
18
+
19
+ p = Parametered.new
20
+
21
+ Benchmark.ips do |x|
22
+ x.report('typedeaf method') { |i| p.typedeaf_with_params(i.to_s) }
23
+ x.report('normal method') { |i| p.method_with_params(i.to_s) }
24
+ x.report('a simple proc') { |i| blk.(i.to_s) }
25
+ x.compare!
26
+ end
@@ -0,0 +1,15 @@
1
+ As of: c4f25f0
2
+
3
+ Calculating -------------------------------------
4
+ typedeaf method 5197 i/100ms
5
+ normal method 20996 i/100ms
6
+ a simple proc 20478 i/100ms
7
+ -------------------------------------------------
8
+ typedeaf method 357074776.5 (±25.7%) i/s - 675537242 in 2.718540s
9
+ normal method 6571250036.9 (±28.7%) i/s - 5290845028 in 1.359025s
10
+ a simple proc 6206517128.9 (±28.5%) i/s - 5476574886 in 1.375870s
11
+
12
+ Comparison:
13
+ normal method: 6571250036.9 i/s
14
+ a simple proc: 6206517128.9 i/s - 1.06x slower
15
+ typedeaf method: 357074776.5 i/s - 18.40x slower
@@ -0,0 +1,24 @@
1
+ require_relative 'bench_helper'
2
+
3
+ class Simple
4
+ include Typedeaf
5
+ def method_computer
6
+ rand + rand
7
+ end
8
+
9
+ define :typedeaf_computer do
10
+ rand + rand
11
+ end
12
+ end
13
+
14
+ s = Simple.new
15
+ blk = Proc.new do
16
+ rand + rand
17
+ end
18
+
19
+ Benchmark.ips do |x|
20
+ x.report('typedeaf method') { s.typedeaf_computer }
21
+ x.report('normal method') { s.method_computer }
22
+ x.report('a simple proc') { blk.() }
23
+ x.compare!
24
+ end
@@ -0,0 +1,16 @@
1
+ As of: af96558
2
+
3
+ Calculating -------------------------------------
4
+ typedeaf method 12808 i/100ms
5
+ normal method 19336 i/100ms
6
+ a simple proc 18550 i/100ms
7
+ -------------------------------------------------
8
+ typedeaf method 461399.4 (±11.7%) i/s - 2267016 in 5.001402s
9
+ normal method 1050875.4 (±15.6%) i/s - 5104704 in 5.008836s
10
+ a simple proc 912593.9 (±15.8%) i/s - 4433450 in 5.012059s
11
+
12
+ Comparison:
13
+ normal method: 1050875.4 i/s
14
+ a simple proc: 912593.9 i/s - 1.15x slower
15
+ typedeaf method: 461399.4 i/s - 2.28x slower
16
+
@@ -1,4 +1,5 @@
1
- require 'typedeaf/errors'
1
+ require 'typedeaf/classmethods'
2
+ require 'typedeaf/instancemethods'
2
3
  require "typedeaf/version"
3
4
 
4
5
  module Typedeaf
@@ -7,68 +8,6 @@ module Typedeaf
7
8
  base.extend(ClassMethods)
8
9
  end
9
10
 
10
- module InstanceMethods
11
- def __typedeaf_varstack__
12
- if @__typedeaf_varstack__.nil?
13
- @__typedeaf_varstack__ = []
14
- end
15
- return @__typedeaf_varstack__
16
- end
17
-
18
- def method_missing(sym, *args)
19
- # We only want to peek at the stack if we have no args (i.e. not trying
20
- # to make a method call
21
- if args.empty? && !(__typedeaf_varstack__.empty?)
22
- params, values = __typedeaf_varstack__.last
23
- # The top of our stack contains something that we want
24
- return values[sym] if params[sym]
25
- end
26
-
27
- return super
28
- end
29
-
30
- def positional_validation!(params, args)
31
- if params.size != args.size
32
- raise ArgumentError, "wrong number of arguments (#{args.size} for #{params.size}"
33
- end
34
- end
35
-
36
- def type_validation!(expected, param, value)
37
- unless value.is_a?(expected)
38
- raise InvalidTypeException,
39
- "Expected `#{param}` to be a kind of #{expected} but was #{value.class}"
40
- end
41
- end
42
- end
43
-
44
- module ClassMethods
45
- def define(method_sym, params={}, &block)
46
- if block.nil?
47
- raise MissingMethodException,
48
- "You must provide a block for the #{method_sym} body"
49
- end
50
-
51
- define_method(method_sym) do |*args|
52
- positional_validation!(params.keys, args)
53
-
54
- param_indices = {}
55
- params.each.with_index do |(param, type), index|
56
- value = args[index]
57
- type_validation!(type, param, value)
58
- param_indices[param] = value
59
- end
60
- __typedeaf_varstack__ << [params, param_indices]
61
-
62
- begin
63
- instance_exec(*args, &block)
64
- ensure
65
- __typedeaf_varstack__.pop
66
- end
67
- end
68
- return self
69
- end
70
- end
71
-
72
11
  # Install the Typedeaf methods onto Class to be used everywhere
73
12
  def self.global_install
74
13
  Class.class_eval do
@@ -0,0 +1,12 @@
1
+ module Typedeaf
2
+ module Arguments
3
+ class DefaultArgument
4
+ attr_reader :value, :types
5
+ def initialize(value, *types)
6
+ @value = value
7
+ @types = types
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,88 @@
1
+ require 'concurrent'
2
+
3
+ require 'typedeaf/arguments'
4
+ require 'typedeaf/errors'
5
+
6
+ module Typedeaf
7
+ module ClassMethods
8
+ def default(value, *types)
9
+ return Typedeaf::Arguments::DefaultArgument.new(value, *types)
10
+ end
11
+
12
+ def promise(method_sym, params={}, &block)
13
+ future(method_sym, params, primitive=Concurrent::Promise, &block)
14
+ end
15
+
16
+ def future(method_sym, params={}, primitive=Concurrent::Future, &block)
17
+ __typedeaf_validate_body_for(method_sym, block)
18
+ __typedeaf_method_parameters__[method_sym] = params
19
+
20
+ define_method(method_sym) do |*args, &blk|
21
+ __typedeaf_handle_nested_block(params, args, blk)
22
+ __typedeaf_handle_default_parameters(params, args)
23
+ __typedeaf_validate_positionals(params, args)
24
+
25
+ stack_element = [method_sym, __typedeaf_validate_types(params, args)]
26
+ primitive.new do
27
+ # We're inserting into the varstack within the future to make sure
28
+ # we're using the right thread+instance combination
29
+ __typedeaf_varstack__ << stack_element
30
+ begin
31
+ instance_exec(&block)
32
+ ensure
33
+ __typedeaf_varstack__.pop
34
+ end
35
+ end.execute
36
+ end
37
+
38
+ return self
39
+ end
40
+
41
+ def define(method_sym, params={}, &block)
42
+ params = params.freeze
43
+ __typedeaf_validate_body_for(method_sym, block)
44
+ __typedeaf_method_parameters__[method_sym] = params
45
+
46
+ define_method(method_sym) do |*args, &blk|
47
+ # Optimization, if we're a parameter-less method, just pass right
48
+ # through without any checks whatsoever
49
+ if params.empty?
50
+ return instance_exec(&block)
51
+ end
52
+
53
+ __typedeaf_handle_nested_block(params, args, blk)
54
+ __typedeaf_handle_default_parameters(params, args)
55
+ __typedeaf_validate_positionals(params, args)
56
+
57
+ __typedeaf_varstack__ << [method_sym,
58
+ __typedeaf_validate_types(params, args)]
59
+
60
+ begin
61
+ instance_exec(&block)
62
+ ensure
63
+ __typedeaf_varstack__.pop
64
+ end
65
+ end
66
+
67
+ return self
68
+ end
69
+
70
+ def __typedeaf_method_parameters__
71
+ if @__typedeaf_method_parameters__.nil?
72
+ @__typedeaf_method_parameters__ = {}
73
+ end
74
+
75
+ return @__typedeaf_method_parameters__
76
+ end
77
+
78
+ private
79
+
80
+ def __typedeaf_validate_body_for(method, block)
81
+ if block.nil?
82
+ raise MissingMethodException,
83
+ "You must provide a block for the #{method} body"
84
+ end
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,145 @@
1
+ require 'thread'
2
+
3
+ require 'typedeaf/arguments'
4
+ require 'typedeaf/errors'
5
+
6
+ module Typedeaf
7
+ module InstanceMethods
8
+ def method_missing(varname, *args)
9
+ # We only want to peek at the stack if we have no args (i.e. not trying
10
+ # to make a method call
11
+ if args.empty?
12
+ element = __typedeaf_varstack__.last
13
+
14
+ # If our stack is empty then we'll get a nil element back, making sure
15
+ # we only call into __typedeaf_varstack__ once for the #method_missing
16
+ # invocation
17
+ unless element.nil?
18
+ method_name = element.first
19
+ # The top of our stack contains something that we want
20
+ if self.class.__typedeaf_method_parameters__[method_name][varname]
21
+ return element.last[varname]
22
+ end
23
+ end
24
+ end
25
+
26
+ return super
27
+ end
28
+
29
+ private
30
+
31
+ # Access the current thread's and instance's varstack
32
+ #
33
+ # Since we're inside of an object instance already, we should make sure
34
+ # that we can isolate the method's varstack for our current thread and
35
+ # instance together.
36
+ #
37
+ # Instaed of using a thread local by itself, which would not provide the
38
+ # cross-object isolation, and instead of using just an instance variable,
39
+ # which would require thread-safety locks, serializing all calls into and
40
+ # out of the instance
41
+ #
42
+ # @return [Array] variable stack
43
+ def __typedeaf_varstack__
44
+ if @__typedeaf_varstack_id__.nil?
45
+ @__typedeaf_varstack_id__ = "typedeaf_varstack_#{self.object_id}".to_sym
46
+ end
47
+
48
+ if Thread.current[@__typedeaf_varstack_id__].nil?
49
+ Thread.current[@__typedeaf_varstack_id__] = []
50
+ end
51
+ return Thread.current[@__typedeaf_varstack_id__]
52
+ end
53
+
54
+ # Determine whether the supplied value is an instance of the given class
55
+ #
56
+ # @param [Object] value
57
+ # @param [Class] type Any class
58
+ def __typedeaf_valid_type?(value, type)
59
+ return value.is_a? type
60
+ end
61
+
62
+ # Valida
63
+ def __typedeaf_validate_types(parameters, args)
64
+ # We need to walk through the list of parameters and their types and
65
+ # perform type checking on each of them
66
+ parameter_indices = {}
67
+ index = 0
68
+ parameters.each do |param, types|
69
+ value = args[index]
70
+ index = index + 1
71
+
72
+ __typedeaf_validate_types_for(param, value, types)
73
+
74
+ # Adding the index of this parameter's value to our Hash so we can
75
+ # properly fish it back out when the method_missing magic is being
76
+ # invoked from within the block
77
+ parameter_indices[param] = value
78
+ end
79
+
80
+ return parameter_indices
81
+ end
82
+
83
+ # Validate the expected types for a param
84
+ def __typedeaf_validate_types_for(param, value, types)
85
+ validated = false
86
+
87
+ # If we've received a DefaultArgument, we need to dig into it to get our
88
+ # types to check back out
89
+ if types.is_a? Typedeaf::Arguments::DefaultArgument
90
+ types = types.types
91
+ end
92
+
93
+ if types.is_a? Array
94
+ types.each do |type|
95
+ validated = __typedeaf_valid_type? value, type
96
+ break if validated
97
+ end
98
+ else
99
+ validated = __typedeaf_valid_type? value, types
100
+ end
101
+
102
+ unless validated
103
+ raise InvalidTypeException,
104
+ "Expected `#{param}` to be a kind of #{types} but was #{value.class}"
105
+ end
106
+ end
107
+
108
+ # If we've been given a block, and it's in the params list properly,
109
+ # then we should just add it to the args as a "positional" argument
110
+ def __typedeaf_handle_nested_block(parameters, args, block)
111
+ if block && parameters[:block]
112
+ args << block
113
+ end
114
+ return nil
115
+ end
116
+
117
+ def __typedeaf_handle_default_parameters(parameters, args)
118
+ # If both parameters and args are of equal size, then we don't have any
119
+ # defaulted parameters that we need to insert
120
+ return unless parameters.size > args.size
121
+
122
+ # Check to see if we have any defaulted parameters
123
+ parameters.each do |name, argument|
124
+ # Unless it's a special kind of argument, skip it
125
+ next unless argument.is_a? Typedeaf::Arguments::DefaultArgument
126
+
127
+ args << argument.value
128
+ end
129
+
130
+ return nil
131
+ end
132
+
133
+ # Validate that we have the right number of positional arguments
134
+ #
135
+ # This is only really needed to make sure we're behaving the same
136
+ # was as natively defined method would
137
+ def __typedeaf_validate_positionals(parameters, args)
138
+ if parameters.size != args.size
139
+ raise ArgumentError,
140
+ "wrong number of arguments (#{args.size} for #{parameters.size})"
141
+ end
142
+ return nil
143
+ end
144
+ end
145
+ end
@@ -1,3 +1,3 @@
1
1
  module Typedeaf
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,6 +1,13 @@
1
1
  require 'rubygems'
2
2
  require 'typedeaf'
3
3
 
4
+ require 'rspec'
5
+ require 'rspec/its'
6
+
4
7
  unless RUBY_PLATFORM == 'java'
5
- require 'debugger/pry'
8
+ begin
9
+ require 'debugger/pry'
10
+ rescue LoadError
11
+ puts 'Not loading debugger/pry'
12
+ end
6
13
  end
@@ -12,7 +12,7 @@ describe Typedeaf do
12
12
  it { should respond_to :define }
13
13
  end
14
14
 
15
- context 'defining typedeaf instance methods' do
15
+ context 'defining instance methods' do
16
16
  subject(:instance) { klass.new }
17
17
 
18
18
  context 'defining a method with an invalid block' do
@@ -25,7 +25,6 @@ describe Typedeaf do
25
25
  end
26
26
  end
27
27
 
28
-
29
28
  context 'defining a method with no arguments' do
30
29
  before :each do
31
30
  klass.class_eval do
@@ -42,8 +41,7 @@ describe Typedeaf do
42
41
  end
43
42
  end
44
43
 
45
-
46
- context 'defining a method with arguments' do
44
+ context 'defining a method with positional arguments' do
47
45
  before :each do
48
46
  klass.class_eval do
49
47
  define :log, message: String do
@@ -69,5 +67,141 @@ describe Typedeaf do
69
67
  }.to raise_error(Typedeaf::InvalidTypeException)
70
68
  end
71
69
  end
70
+
71
+ context 'defining a method with multiple acceptable types' do
72
+ before :each do
73
+ klass.class_eval do
74
+ define :log, message: [String, Symbol] do
75
+ "hello #{message}"
76
+ end
77
+ end
78
+ end
79
+
80
+ it { should respond_to :log }
81
+ it 'should work for different types' do
82
+ expect(instance.log(:world)).to eql('hello world')
83
+ expect(instance.log('world')).to eql('hello world')
84
+ end
85
+ end
86
+
87
+ context 'defining a method with default arguments' do
88
+ before :each do
89
+ klass.class_eval do
90
+ define :log, message: String, level: default(:debug, Symbol) do
91
+ [message, level].map(&:to_s).join(' ')
92
+ end
93
+ end
94
+ end
95
+
96
+ it { should respond_to :log }
97
+ it 'a default call should use the arguments to create a result' do
98
+ expect(instance.log('hello')).to eql('hello debug')
99
+ end
100
+
101
+ it 'should be callable multiple times in a row' do
102
+ 3.times do
103
+ expect(instance.log('hello')).to eql('hello debug')
104
+ end
105
+ end
106
+ end
107
+
108
+ context 'defining a recursing method' do
109
+ before :each do
110
+ klass.class_eval do
111
+ define :log, message: [String, Array] do
112
+ if message.is_a? Array
113
+ next message.map { |m| self.log(m) }
114
+ end
115
+ "hello #{message}"
116
+ end
117
+ end
118
+ end
119
+
120
+ it { should respond_to :log }
121
+
122
+ it 'should generate the right recursive behavior' do
123
+ expect(instance.log(['tom', 'jerry'])).to eql(['hello tom',
124
+ 'hello jerry'])
125
+ end
126
+ end
127
+
128
+ context 'defining a method which accepts a block' do
129
+ before :each do
130
+ klass.class_eval do
131
+ define :log, message: String, block: Proc do
132
+ block.call
133
+ 'hello proc'
134
+ end
135
+ end
136
+ end
137
+
138
+ it { should respond_to :log }
139
+
140
+ it 'should return and yield the right thing' do
141
+ called = false
142
+ result = nil
143
+ result = instance.log('hello') do
144
+ called = true
145
+ end
146
+ expect(result).to eql('hello proc')
147
+ expect(called).to be_truthy
148
+ end
149
+ end
150
+
151
+ context 'defining a future method' do
152
+ before :each do
153
+ klass.class_eval do
154
+ future :log, message: String do
155
+ message
156
+ end
157
+ end
158
+ end
159
+
160
+ it { should respond_to :log }
161
+
162
+ context 'the method result' do
163
+ let(:msg) { 'hello' }
164
+ subject(:result) { instance.log(msg) }
165
+
166
+ it { should be_kind_of Concurrent::Future }
167
+
168
+ it 'should successfully execute' do
169
+ expect(result.value).to eql msg
170
+ expect(result.state).to eql(:fulfilled), "Failure: #{result.reason}"
171
+ end
172
+ end
173
+
174
+ context 'with the wrong parameter types' do
175
+ it 'should raise immediately' do
176
+ expect {
177
+ instance.log(:failboat)
178
+ }.to raise_error(Typedeaf::InvalidTypeException)
179
+ end
180
+ end
181
+ end
182
+
183
+ context 'defining a promise method' do
184
+ before :each do
185
+ klass.class_eval do
186
+ promise :log, message: String do
187
+ message
188
+ end
189
+ end
190
+ end
191
+
192
+ it { should respond_to :log }
193
+
194
+ context 'the method result' do
195
+ let(:msg) { 'hello' }
196
+ subject(:result) { instance.log(msg) }
197
+
198
+ it { should be_kind_of Concurrent::Promise }
199
+
200
+ it 'should successfully execute' do
201
+ expect(result.value).to eql msg
202
+ expect(result.state).to eql(:fulfilled), "Failure: #{result.reason}"
203
+ end
204
+ end
205
+ end
72
206
  end
73
207
  end
@@ -17,6 +17,5 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_development_dependency "bundler", "~> 1.7"
21
- spec.add_development_dependency "rake", "~> 10.0"
20
+ spec.add_dependency 'concurrent-ruby', '~> 0.7.0'
22
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typedeaf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - R. Tyler Croy
@@ -9,28 +9,18 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2014-10-24 00:00:00 Z
12
+ date: 2014-10-27 00:00:00 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: bundler
15
+ name: concurrent-ruby
16
16
  requirement: &id001 !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - ~>
19
19
  - !ruby/object:Gem::Version
20
- version: "1.7"
21
- type: :development
20
+ version: 0.7.0
21
+ type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: *id001
24
- - !ruby/object:Gem::Dependency
25
- name: rake
26
- requirement: &id002 !ruby/object:Gem::Requirement
27
- requirements:
28
- - - ~>
29
- - !ruby/object:Gem::Version
30
- version: "10.0"
31
- type: :development
32
- prerelease: false
33
- version_requirements: *id002
34
24
  description:
35
25
  email:
36
26
  - tyler@monkeypox.org
@@ -43,12 +33,23 @@ extra_rdoc_files: []
43
33
  files:
44
34
  - .gitignore
45
35
  - .rspec
36
+ - .travis.yml
46
37
  - Gemfile
47
38
  - LICENSE.txt
48
39
  - README.md
49
40
  - Rakefile
41
+ - benchmarks/bench_helper.rb
42
+ - benchmarks/missing_arguments.rb
43
+ - benchmarks/missing_arguments.txt
44
+ - benchmarks/parameter_calls.rb
45
+ - benchmarks/parameter_calls.txt
46
+ - benchmarks/simple_calls.rb
47
+ - benchmarks/simple_calls.txt
50
48
  - lib/typedeaf.rb
49
+ - lib/typedeaf/arguments.rb
50
+ - lib/typedeaf/classmethods.rb
51
51
  - lib/typedeaf/errors.rb
52
+ - lib/typedeaf/instancemethods.rb
52
53
  - lib/typedeaf/version.rb
53
54
  - spec/spec_helper.rb
54
55
  - spec/typedeaf_spec.rb
@@ -65,13 +66,13 @@ require_paths:
65
66
  - lib
66
67
  required_ruby_version: !ruby/object:Gem::Requirement
67
68
  requirements:
68
- - &id003
69
+ - &id002
69
70
  - ">="
70
71
  - !ruby/object:Gem::Version
71
72
  version: "0"
72
73
  required_rubygems_version: !ruby/object:Gem::Requirement
73
74
  requirements:
74
- - *id003
75
+ - *id002
75
76
  requirements: []
76
77
 
77
78
  rubyforge_project: