diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..2f67cda --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--order random --fail-fast --color --format doc diff --git a/Gemfile b/Gemfile index 28a19ba..b8b4e69 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,12 @@ source 'https://rubygems.org' # Specify your gem's dependencies in typedeaf.gemspec gemspec + +gem 'facets' + +group :development do + gem 'rake' + gem 'rspec' + gem 'pry' + gem 'debugger-pry', :platform => :mri +end diff --git a/Rakefile b/Rakefile index 809eb56..9d140af 100644 --- a/Rakefile +++ b/Rakefile @@ -1,2 +1,7 @@ require "bundler/gem_tasks" +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new + +task :default => [:spec, :build] diff --git a/lib/typedeaf.rb b/lib/typedeaf.rb index 07a07d6..0bd9f62 100644 --- a/lib/typedeaf.rb +++ b/lib/typedeaf.rb @@ -1,5 +1,71 @@ +require 'typedeaf/errors' require "typedeaf/version" +require 'continuation' +require 'facets/binding' + module Typedeaf - # Your code goes here... + def self.included(base) + base.send(:include, InstanceMethods) + base.extend(ClassMethods) + end + + module InstanceMethods + def __typedeaf_varstack__ + if @__typedeaf_varstack__.nil? + @__typedeaf_varstack__ = [] + end + return @__typedeaf_varstack__ + end + + def method_missing(sym, *args) + # We only want to peek at the stack if we have no args (i.e. not trying + # to make a method call + if args.empty? && !(__typedeaf_varstack__.empty?) + params, values = __typedeaf_varstack__.last + # The top of our stack contains something that we want + return values[sym] if params[sym] + end + + return super + end + end + + module ClassMethods + def define(method_sym, params={}, &block) + if block.nil? + raise MissingMethodException, + "You must provide a block for the #{method_sym} body" + end + + define_method(method_sym) do |*args| + param_indices = {} + params.each.with_index do |(key, value), index| + param_indices[key] = args[index] + end + __typedeaf_varstack__ << [params, param_indices] + begin + instance_exec(*args, &block) + ensure + __typedeaf_varstack__.pop + end + end + return self + end + end + + # Install the Typedeaf methods onto Class to be used everywhere + def self.global_install + Class.class_eval do + include Typedeaf + + alias_method :old_inherited, :inherited + def inherited(subclass) + subclass.class_eval do + include Typedeaf + end + return old_inherited(subclass) + end + end + end end diff --git a/lib/typedeaf/errors.rb b/lib/typedeaf/errors.rb new file mode 100644 index 0000000..40dc702 --- /dev/null +++ b/lib/typedeaf/errors.rb @@ -0,0 +1,4 @@ + +module Typedeaf + class MissingMethodException < StandardError; end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..e0ad9f0 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,6 @@ +require 'rubygems' +require 'typedeaf' + +unless RUBY_PLATFORM == 'java' + require 'debugger/pry' +end diff --git a/spec/typedeaf_spec.rb b/spec/typedeaf_spec.rb new file mode 100644 index 0000000..a067d15 --- /dev/null +++ b/spec/typedeaf_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +describe Typedeaf do + subject(:klass) do + Class.new do + include Typedeaf + end + end + + context 'with a new class' do + it { should be_kind_of Class } + it { should respond_to :define } + end + + context 'defining typedeaf instance methods' do + subject(:instance) { klass.new } + + context 'defining a method with an invalid block' do + it 'should raise a MissingMethodException' do + expect { + klass.class_eval do + define :log + end + }.to raise_error(Typedeaf::MissingMethodException) + end + end + + + #context 'defining a method with no arguments' do + # before :each do + # klass.class_eval do + # tdef :log do + # 'hello rspec' + # end + # end + # end + + # it { should respond_to :log } + + # it 'should return the right value when invoked' do + # expect(instance.log).to eql('hello rspec') + # end + #end + + + context 'defining a method with arguments' do + before :each do + klass.class_eval do + define :log, message: String do + "hello #{message}" + end + end + end + + it { should respond_to :log } + it 'should use the arguments to generate a result' do + puts "instance: #{instance.object_id}" + expect(instance.log('world')).to eql('hello world') + end + end + end +end