0

I am following Michael Hartl's tutorial, and trying to implement the reply twitter-like functionality, ie. "@122-john-smith: hello there" should be a reply to user 122.

I first tried filtering the "@XXX-AAA-AAA" part using a before_filter, but I decided to try it first in the very same Micropost#create action. So far I've got this MicropostController:

class MicropostsController < ApplicationController
    before_filter :signed_in_user, only: [:create, :destroy]
    before_filter :correct_user, only: [:destroy]
    #before_filter :reply_to_user, only: [:create]

    def index
    end

    def create
        @micropost=current_user.microposts.build(params[:micropost])
        #Rails.logger.info "hoooola"
        regex=/\A@(\d)+(\w|\-|\.)+/i
        [email protected]
        isResponse=message.match(regex)[0].match(/\d+/)[0]
        @micropost.response=isResponse
        if @micropost.save
            flash[:success]="Micropost created!"
            redirect_to root_path
        else
            @feed_items=[]
            render 'static_pages/home'
        end
    end

    def destroy
        @micropost.destroy
        redirect_to root_path
    end

    private

    def correct_user
        @micropost = current_user.microposts.find_by_id(params[:id])
        redirect_to root_path if @micropost.nil?
    end

    def reply_to_user
        regex=/\A@(\d)+(\w|\-|\.)+/i
        #I use [0] cause the output of match is a MatchData class with lots of bs
        mtch=params[:micropost][:content].match(regex)[0]
        #puts mtch
        #@micropost=current_user.microposts.build(params[:micropost])
        if mtch != nil
            user_id=mtch.match(/\d+/)[0]
            @replied_user=User.find(user_id)
            @micropost.response=user_id unless @replied_user.nil?
        end
    end
end

And this is the snippet test I'm trying to pass:

require 'spec_helper'

describe "MicropostPages" do
    subject { page }
    let(:user) { FactoryGirl.create(:user) }
    before { valid_signin user }
    describe "micropost creation" do
        before { visit root_path }
        describe "with invalid information" do
            it "should not create a micropost" do
                expect { click_button "Post" }.should_not change(Micropost,
                                                                                        :count)
            end
            describe "error messages" do
                before { click_button "Post" }
                it { should have_content('error') }
            end
        end
        describe "with valid information" do
            before { fill_in 'micropost_content', with: "Lorem ipsum" }
            it "should create a micropost" do
                expect { click_button "Post" }.should change(Micropost,
                                         :count).by(1)
            end
        end
    end
    ...

end

If I run these tests I get the follwing error:

Failures:

  1) MicropostPages micropost creation with invalid information should not create a micropost
     Failure/Error: expect { click_button "Post" }.should_not change(Micropost, :count)
     NoMethodError:
       undefined method `[]' for nil:NilClass
     # ./app/controllers/microposts_controller.rb:14:in `create'
     # (eval):2:in `click_button'
     # ./spec/requests/micropost_pages_spec.rb:11:in `block (5 levels) in <top (required)>'
     # ./spec/requests/micropost_pages_spec.rb:11:in `block (4 levels) in <top (required)>'

  2) MicropostPages micropost creation with invalid information error messages 
     Failure/Error: before { click_button "Post" }
     NoMethodError:
       undefined method `[]' for nil:NilClass
     # ./app/controllers/microposts_controller.rb:14:in `create'
     # (eval):2:in `click_button'
     # ./spec/requests/micropost_pages_spec.rb:14:in `block (5 levels) in <top (required)>'

However if I modify the tests and comment out all the @XXX filtering in the Micropost#create action:

    def create
        @micropost=current_user.microposts.build(params[:micropost])
        #Rails.logger.info "hoooola"
        #regex=/\A@(\d)+(\w|\-|\.)+/i
        #[email protected]
        #isResponse=message.match(regex)[0].match(/\d+/)[0]
        #@micropost.response=isResponse
        if @micropost.save
            flash[:success]="Micropost created!"
            redirect_to root_path
        else
            @feed_items=[]
            render 'static_pages/home'
        end
    end

The tests pass just fine and the new Micropost is not a Nil object.

It can't seem to find an explanation here.

4
  • 1
    Maybe its the regular expression match(regex)[0].match(/\d+/)[0] that causes the issue... Commented Oct 10, 2013 at 11:05
  • I tried it using irb and It returned the desired ID. Besides, shouldn't it in the worst case just write Nil in the @micropost.response field? Commented Oct 10, 2013 at 11:07
  • 1
    no, because you are accessing it with [0] this will throw the exact same error if the regex doesn't match. Commented Oct 10, 2013 at 11:09
  • oh, you are so righ! It indeed throws the same error when the regex doesn't match. Thank you. If you post an answer I can mark it as correct answer. Commented Oct 10, 2013 at 11:14

1 Answer 1

1

The error comes from this line:

isResponse=message.match(regex)[0].match(/\d+/)[0]

Check if your two match calls actually match correctly. If the pattern is not found in your string, nil is returned and the [0] call is made on nil. There's two instances in this line alone where this could happen.

Try to spread it out over several lines and check the return values of your matches or extend your Regex to properly check the pattern in one go.

Sign up to request clarification or add additional context in comments.

5 Comments

It seems this is exactly the issue, as also pointed out by @spas. I completely missed that. Also, I wonder what is the appropiate way to debug Controllers. I mean, how would you check the result of each .match if it's inside a controller? I mean I know how to do it in irb, but can't find an appropiate way via testing.
Well your tests are correctly performing the POST with all form params and your create-action gets executed. You should provide all sorts of input params as you need to expect the users to enter terribly wrong data by accident or sometimes on purpose. Check the results of your tests accordingly. The create call mustn't throw errors for any input. If it does, your validation checks for the correctness of input is not robust enough and you need to add additional conditions. This is not any different from calling your regex match on random values in irb.
That makes sense, thanks. Although I was referring to a more explicit debugging à la puts variablename. So I can see actual contents of variables in the terminal.
Ah sorry. Just printing stuff to the console is very crude by todays standards. You can use the debug logs to print some values. You will need to look up how to use the Ruby Logger (or similar frameworks) yourself though as a simple comment wouldn't be enough to explain. If you have advanced IDEs like RubyMine, you can simply set a breakpoint in your method and then execute arbitrary code when execution stops there when in debug mode.
Oh, that's what I thought, only Ruby Logger. I will have to stick to that and keep grep*-ing* the tail of the log/test.log with some keywords. thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.