iosruby-on-railsrspecdevicetoken

Rails Associations Rspec


I'm struggling to understand the relationship that owner = create(:user, device_token: device_token) has to owner: {device_token: device_token}, I usually use user_id for this association. 2. What is the device_token method in the controller is doing.

describe 'POST /v1/events' do
   it 'saves the address, lat, lon, name, and started_at date' do
     date = Time.zone.now
     device_token = '123abcd456xyz'
     owner = create(:user, device_token: device_token)

     post '/v1/events', {
       address: '123 Example St.',
       ended_at: date,
       lat: 1.0,
       lon: 1.0,
       name: 'Fun Place!!',
       started_at: date,
       owner: {
         device_token: device_token
       }
      }.to_json, { 'Content-Type' => 'application/json' }

      event = Event.last
        expect(response_json).to eq({ 'id' => event.id })
        expect(event.address).to eq '123 Example St.'
        expect(event.ended_at.to_i).to eq date.to_i
        expect(event.lat).to eq 1.0
        expect(event.lon).to eq 1.0
        expect(event.name).to eq 'Fun Place!!'
        expect(event.started_at.to_i).to eq date.to_i
        expect(event.owner).to eq owner
  end
end

Controller Code:

def create
  @event = Event.new(event_params)

  if @event.save
    render
  end
end


private

  def event_params
    {
      address: params[:address],
      ended_at: params[:ended_at],
      lat: params[:lat],
      lon: params[:lon],
      name: params[:name],
      started_at: params[:started_at],
      owner: user
    }
  end

  def user
    User.find_or_create_by(device_token: device_token)
  end

  def device_token
    params[:owner].try(:[], :device_token)
  end
end

Solution

  • There's a number of ways you can identify uniquely identify a record in a database. Using the id field is the most common - but if you've got another way to uniquely identify a user, then you can use that, too. Normally, you don't show a user what their ID number is in the database. But, if you want to give them a way to uniquely identify themselves, you could create another field which is unique for each user - such as a membership_number or similar. It seems like in your case, device_token is a field that uniquely identifies a user.

    So, your database cares about the user_id field - that's what it uses to tie an Event to a specific User (aka, the owner). If your users knew their id, then they could pass in that, rather than their device_token, and everything would be fine. But they don't know it.

    So, they pass in their devise_token. You use that to fetch the user from the database, and then you know that user's id. Then, you can store that user's id in the user_id field of your Event model.

    def user
      User.find_or_create_by(device_token: device_token)
    end
    

    This method is the one that gets a user based on a devise_token. And then this method:

    def event_params
      {
        address: params[:address],
        ended_at: params[:ended_at],
        lat: params[:lat],
        lon: params[:lon],
        name: params[:name],
        started_at: params[:started_at],
        owner: user
      }
    end
    

    In particular, the line: owner: user calls that method above. From that point, Rails handles it under the hood and makes sure your user_id is set correctly.

    UPDATE AFTER YOUR COMMENT:

    device_token is being passed in as a parameter. It is also the name of a field in the User model. So, a single row in the user table might look like this:

    id: 24, first_name: fred, last_name: flintstone, device_token: 123abc, address: bedrock, etc.

    the method:

    def user
      User.find_or_create_by(device_token: device_token)
    end
    

    is saying: go to the User's table in the database, try to find a User which has a device_token that has the value that was passed in as a parameter, and if we can't find one, then create one.

    So in this line: User.find_or_create_by(device_token: device_token), the first reference to device_token is the key of a hash, and it refers to the field called device_token in your User model.

    The second reference to device_token is a call to this method:

    def device_token
      params[:owner].try(:[], :device_token)
    end
    

    which fetches the device_token from the parameters passed in. This method basically says: Look in the params hash at the value inside the owner key. See if the owner key contains a device_token. If it does, return that device_token, and if it doesn't return nil. It does this using the try method, which you can read more about here: http://apidock.com/rails/Object/try