Accessing a Match record, which belongs to a Run record, which belongs to a User record

47 Views Asked by At

I am trying to set up a little Rails app where the intention is basically to pair runners together that want to go on a run at a similar date/time, with a similar distance and pace. So far my app has three models: Users, Runs, and Matches. A User can create a Run record. When a Run record is created, the run_controller does a check to see if there are any "similar runs" already in existence that were created by other users; if similar runs exist, then a Match record is also created in the database at the same time the run record is created. Run records and Match records are already being created as expected in my project. I am now trying to add onto the user's homepage view so that it displays a list of Match records that pertain to that User, i.e. show a User the different people they might be able to do a given run with and some details of that matching run (other user's name, the distance and pace of the run they entered to the database).

I am logged in now as user1, who has a match record with user2. I've verified this in my Rails console: • when I type Match.last I get back this active record object:

\<Match:0x0000000111abecf0
id: 13,
run1: 30,
run2: 29,
created_at: Sun, 19 Nov 2023 19:03:15.653073000 UTC +00:00,
updated_at: Sun, 19 Nov 2023 19:03:15.653073000 UTC +00:00\>

• when I type Run.find_by(id: 29), I get back

\<Run:0x0000000111a7bb80
id: 29,
distance: 6.0,
pace: 570,
user_id: 2,
created_at: Sun, 19 Nov 2023 18:45:13.667503000 UTC +00:00,
updated_at: Sun, 19 Nov 2023 18:45:13.667503000 UTC +00:00,
start_time_beginning: Sat, 25 Nov 2023 13:15:00.000000000 UTC +00:00,
start_time_end: Sat, 25 Nov 2023 17:15:00.000000000 UTC +00:00\>

• when I type Run.find_by(id: 30), I get back

\<Run:0x0000000106e90d20
id: 30,
distance: 6.0,
pace: 570,
user_id: 1,
created_at: Sun, 19 Nov 2023 19:03:15.561492000 UTC +00:00,
updated_at: Sun, 19 Nov 2023 19:03:15.561492000 UTC +00:00,
start_time_beginning: Sat, 25 Nov 2023 14:03:00.000000000 UTC +00:00,
start_time_end: Sat, 25 Nov 2023 18:03:00.000000000 UTC +00:00\>

So here I have proof that user1 owns Run with id 30, and there is a match where one of the runs is 30. So theoretically, that Match object (which has an ID of 13 shown above) belongs to user1. But when I try to render a list of Matches for the user1 on their home page, if current_user.matches.present? is returning false.

Below I will paste the contents of my view file, Run, User, and Match models, runs_controller, and schema:

view file: app/views/pages/index.html.erb

<p>Pages#index...Find me in app/views/pages/index.html.erb</p>

<% if user_signed_in? %>
  <h1>Welcome home, <%= current_user.email %> </h1>
  <%= link_to "Submit new run request", new_run_path %>
  <%= button_to 'Sign out', destroy_user_session_path, method: :delete %>

  <h3>Find a running buddy</h3>
  <% if current_user.matches.present? %>
    <ul>
      <% current_user.matches.each do |match| %>
        <li>
          Match ID: <%= match.id %>
        </li>
      <% end %>
    </ul>
  <% else %>
    <p>No matches found</p>
  <% end %>
<% else %>
  <p>Home, but actually the pages index page</p>
  <%= link_to 'Sign in', new_user_session_path %>
<% end %>

app/models/user.rb:

class User < ApplicationRecord
  has_many :runs
  has_many :matches, ->(user) { 
    unscope(where: :user_id) 
      .joins('INNER JOIN runs AS run1 ON run1.id = matches.run1')
      .joins('INNER JOIN runs AS run2 ON run2.id = matches.run2')
      .joins('INNER JOIN users AS user1 ON user1.id = run1.user_id')
      .joins('INNER JOIN users AS user2 ON user2.id = run2.user_id')
      .where('user1.id = ? OR user2.id = ?', user.id, user.id)
    }, class_name: 'Match', foreign_key: 'run1'
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
end

app/models/match.rb

class Match < ApplicationRecord
  belongs_to :run1, class_name: 'Run', foreign_key: 'run1'
  belongs_to :run2, class_name: 'Run', foreign_key: 'run2'

  validates :run1, uniqueness: { scope: :run2 }
  validates :run2, uniqueness: { scope: :run1 }
end

app/models/run.rb

class Run < ApplicationRecord
  belongs_to :user
  validates :user, presence: true
  has_many :matches, foreign_key: 'run1_id'
end

app/controllers/runs_controller.rb

class RunsController < ApplicationController
  def new
    @run = Run.new
  end

  def create
    @run = current_user.runs.new(run_params)
    @run.pace = pace_per_mile_in_seconds(params[:run][:minutes], params[:run][:seconds])
    if @run.save!
      find_and_create_matches(@run)
      redirect_to root_path, notice: "Run buddy request submitted!"
    else
      flash.now[:alert] = @run.errors.full_messages.join(', ')
      render :new
    end
  end

  private

  def pace_per_mile_in_seconds(minutes, seconds)
    (minutes.to_i * 60) + seconds.to_i
  end

  def run_params
    params.require(:run).permit(:start_time_beginning, :start_time_end, :distance)
  end

  def find_and_create_matches(new_run)
    min_distance = 0.8 * new_run.distance
    max_distance = 1.2 * new_run.distance
    min_pace = new_run.pace - 30
    max_pace = new_run.pace + 30

    similar_runs = Run.where(
      "distance >= ? AND distance <= ? AND pace >= ? AND pace <= ?",
      min_distance, max_distance,
      min_pace, max_pace
    ).where.not(id: new_run.id)

    similar_runs.each do |run|
      if new_run.user != run.user && !(Match.exists?(run1: run, run2: new_run))
        Match.create(run1: new_run, run2: run)
      end
    end
  end
end 

How can I correctly set up my model associations and/or write code in my view file so that I can access the Match records that belong to the Runs that belong to the User who's currently logged in? Also, in Rails console when I type user1.matches (after storing the active record object for user1 in the variable user1), I get back

.asdf/installs/ruby/3.2.1/lib/ruby/gems/3.2.0/gems/activemodel-7.0.7/lib/active_model/attribute_methods.rb:450:in `method_missing': undefined method `matches' for #<User id: 1, email: "[email protected]", created_at: "2023-08-19 03:21:39.137323000 +0000", updated_at: "2023-11-16 23:56:06.657263000 +0000"> (NoMethodError)
ActiveRecord::Schema[7.0].define(version: 2023_11_17_004741) do
  enable_extension "plpgsql"

  create_table "matches", force: :cascade do |t|
    t.bigint "run1"
    t.bigint "run2"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "runs", force: :cascade do |t|
    t.float "distance"
    t.integer "pace"
    t.bigint "user_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "start_time_beginning"
    t.datetime "start_time_end"
    t.index ["user_id"], name: "index_runs_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

  add_foreign_key "runs", "users"
end

I looked at the Rails Association docs and tried to find an association that matched what I'm trying to do. I thought has_many :through (https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association) looked promising, but then I realized that my use case didn't exactly match their use case: Their intermediary model is appointments, and mine would be runs (I think). I their case, an appointment can belong to only one physician and one patient, but in my case, a Run would belong to one User but could belong to multiple Matches.

0

There are 0 best solutions below