typedeaf 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: