Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix problem to catch NoMethodError from non receiver object #12

Merged
merged 12 commits into from
Feb 4, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: ruby
bundler_args: --without development
cache: bundler
rvm:
- 2.1.0
- 2.0.0
Expand All @@ -9,4 +9,4 @@ rvm:
- ree
- jruby-18mode
- jruby-19mode
- rbx-2.2.0
- rbx
13 changes: 0 additions & 13 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,3 @@ source 'https://rubygems.org'

# Specify gem's dependencies in docile.gemspec
gemspec

# Explicitly require test gems for Travis CI, since we're excluding dev dependencies
group :test do
gem 'rake', '~> 0.9.2'
gem 'rspec', '~> 2.11.0'
gem 'mime-types', '~> 1.25.1'
gem 'coveralls', :require => false

platform :rbx do
gem 'rubysl' # Since 2.2.0, Rubinius needs Ruby standard lib as gem
gem 'rubinius-coverage' # Coverage tooling for SimpleCov on Rubinius
end
end
22 changes: 15 additions & 7 deletions docile.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,22 @@ Gem::Specification.new do |s|
s.require_paths = %w(lib)

# Running rspec tests from rake
s.add_development_dependency 'rake', '~> 0.9.2'
s.add_development_dependency 'rspec', '~> 2.11.0'
s.add_development_dependency 'rake', '~> 10.1.1'
s.add_development_dependency 'rspec', '~> 2.14.0'
s.add_development_dependency 'mime-types', '~> 1.25.1'

# Github flavored markdown in YARD documentation
# http://blog.nikosd.com/2011/11/github-flavored-markdown-in-yard.html
s.add_development_dependency 'yard'
s.add_development_dependency 'redcarpet'
s.add_development_dependency 'github-markup'
if defined?(RUBY_ENGINE) && 'rbx' == RUBY_ENGINE
s.add_development_dependency 'rubysl'
s.add_development_dependency 'rubinius-coverage'
end

if !(defined?(RUBY_ENGINE) && 'jruby' == RUBY_ENGINE)
# Github flavored markdown in YARD documentation
# http://blog.nikosd.com/2011/11/github-flavored-markdown-in-yard.html
s.add_development_dependency 'yard'
s.add_development_dependency 'redcarpet', '2.3.0' # because 1.8
s.add_development_dependency 'github-markup'
end

# Coveralls test coverage tool
s.add_development_dependency 'coveralls'
Expand Down
8 changes: 5 additions & 3 deletions lib/docile/fallback_context_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ def instance_variables
# Proxy all methods, excluding {NON_PROXIED_METHODS}, first to `receiver`
# and then to `fallback` if not found.
def method_missing(method, *args, &block)
@__receiver__.__send__(method.to_sym, *args, &block)
rescue ::NoMethodError => _
@__fallback__.__send__(method.to_sym, *args, &block)
if @__receiver__.respond_to?(method.to_sym)
@__receiver__.__send__(method.to_sym, *args, &block)
else
@__fallback__.__send__(method.to_sym, *args, &block)
end
end
end
end
88 changes: 52 additions & 36 deletions spec/docile_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ def execute_dsl_against_array
end

it 'executes the block against the DSL context object' do
array.should == [1, 3]
expect(array).to eq([1, 3])
end

it 'returns the DSL object after executing block against it' do
result.should == array
expect(result).to eq(array)
end

it "doesn't proxy #__id__" do
Docile.dsl_eval(array) { __id__.should_not == array.__id__ }
Docile.dsl_eval(array) { expect(__id__).not_to eq(array.__id__) }
end

it "raises NoMethodError if the DSL object doesn't implement the method" do
Expand Down Expand Up @@ -60,7 +60,7 @@ def execute_dsl_against_builder_and_call_build
end

it 'returns correctly built object' do
result.should == Pizza.new(true, false, true, :extra)
expect(result).to eq(Pizza.new(true, false, true, :extra))
end
end

Expand Down Expand Up @@ -93,88 +93,104 @@ def parameterized(*args, &block)

it 'passes parameters to the block' do
parameterized(1,2,3) do |x,y,z|
x.should == 1
y.should == 2
z.should == 3
expect(x).to eq(1)
expect(y).to eq(2)
expect(z).to eq(3)
end
end

it 'finds parameters before methods' do
parameterized(1) { |a| a.should == 1 }
parameterized(1) { |a| expect(a).to eq(1) }
end

it 'find outer dsl parameters in inner dsl scope' do
parameterized(1,2,3) do |a,b,c|
inner_with_params(c) do |d,e|
a.should == 1
b.should == 2
c.should == 3
d.should == c
e.should == :foo
expect(a).to eq(1)
expect(b).to eq(2)
expect(c).to eq(3)
expect(d).to eq(c)
expect(e).to eq(:foo)
end
end
end
end

class DSLWithNoMethod
def initialize(b); @b = b; end
attr_accessor :b
def push_element
@b.push 1
end
end

context 'when DSL have NoMethod error inside' do
it 'raise error from nil' do
Docile.dsl_eval(DSLWithNoMethod.new(nil)) do
expect { push_element }.to raise_error(NoMethodError, /undefined method `push' (for|on) nil:NilClass/)
end
end
end

context 'when DSL blocks are nested' do

context 'method lookup' do
it 'finds method of outer dsl in outer dsl scope' do
outer { a.should == 'a' }
outer { expect(a).to eq('a') }
end

it 'finds method of inner dsl in inner dsl scope' do
outer { inner { b.should == 'b' } }
outer { inner { expect(b).to eq('b') } }
end

it 'finds method of outer dsl in inner dsl scope' do
outer { inner { a.should == 'a' } }
outer { inner { expect(a).to eq('a') } }
end

it "finds method of block's context in outer dsl scope" do
def c; 'c'; end
outer { c.should == 'c' }
outer { expect(c).to eq('c') }
end

it "finds method of block's context in inner dsl scope" do
def c; 'c'; end
outer { inner { c.should == 'c' } }
outer { inner { expect(c).to eq('c') } }
end

it 'finds method of outer dsl in preference to block context' do
def a; 'not a'; end
outer { a.should == 'a' }
outer { inner { a.should == 'a' } }
outer { expect(a).to eq('a') }
outer { inner { expect(a).to eq('a') } }
end
end

context 'local variable lookup' do
it 'finds local variable from block context in outer dsl scope' do
foo = 'foo'
outer { foo.should == 'foo' }
outer { expect(foo).to eq('foo') }
end

it 'finds local variable from block definition in inner dsl scope' do
bar = 'bar'
outer { inner { bar.should == 'bar' } }
outer { inner { expect(bar).to eq('bar') } }
end
end

context 'instance variable lookup' do
it 'finds instance variable from block definition in outer dsl scope' do
@iv1 = 'iv1'; outer { @iv1.should == 'iv1' }
@iv1 = 'iv1'; outer { expect(@iv1).to eq('iv1') }
end

it "proxies instance variable assignments in block in outer dsl scope back into block's context" do
@iv1 = 'foo'; outer { @iv1 = 'bar' }; @iv1.should == 'bar'
@iv1 = 'foo'; outer { @iv1 = 'bar' }; expect(@iv1).to eq('bar')
end

it 'finds instance variable from block definition in inner dsl scope' do
@iv2 = 'iv2'; outer { inner { @iv2.should == 'iv2' } }
@iv2 = 'iv2'; outer { inner { expect(@iv2).to eq('iv2') } }
end

it "proxies instance variable assignments in block in inner dsl scope back into block's context" do
@iv2 = 'foo'; outer { inner { @iv2 = 'bar' } }; @iv2.should == 'bar'
@iv2 = 'foo'; outer { inner { @iv2 = 'bar' } }; expect(@iv2).to eq('bar')
end
end

Expand Down Expand Up @@ -224,7 +240,7 @@ def send_request(path, request)

def x(y) ; "Got a #{y}"; end
respond '/third' do |third|
x(third).should == 'Got a third thing'
expect(x(third)).to eq('Got a third thing')
end

fourth = nil
Expand All @@ -237,9 +253,9 @@ def x(y) ; "Got a #{y}"; end
send_request '/third', 'third thing'
send_request '/params', :b

@first.should == 1
@second.should == 'Got a new ten speed'
fourth.should == 2
expect(@first).to eq(1)
expect(@second).to eq('Got a new ten speed')
expect(fourth).to eq(2)
end

end
Expand All @@ -260,11 +276,11 @@ def execute_non_mutating_dsl_against_string
end

it "doesn't modify the original string" do
original.should == "I'm immutable!"
expect(original).to eq("I'm immutable!")
end

it 'chains the commands in the block against the DSL context object' do
result.should == "!ELBATUMMI M'I"
expect(result).to eq("!ELBATUMMI M'I")
end
end

Expand All @@ -280,7 +296,7 @@ def execute_non_mutating_dsl_against_number
end

it 'chains the commands in the block against the DSL context object' do
result.should == 42
expect(result).to eq(42)
end
end
end
Expand All @@ -307,15 +323,15 @@ def type_of_ivar_names_on_this_ruby
end

it 'returns proxied instance variables' do
subject.map(&:to_sym).should include(:@foo)
expect(subject.map(&:to_sym)).to include(:@foo)
end

it "doesn't return non-proxied instance variables" do
subject.map(&:to_sym).should_not include(*excluded)
expect(subject.map(&:to_sym)).not_to include(*excluded)
end

it 'preserves the type (String or Symbol) of names on this ruby version' do
actual_type_of_names.should == expected_type_of_names
expect(actual_type_of_names).to eq(expected_type_of_names)
end
end

Expand Down
6 changes: 6 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@
$LOAD_PATH.unshift lib_dir unless $LOAD_PATH.include?(lib_dir)

require 'docile'

RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :expect
end
end