Rails - Creating a GettingStarted Controller - ruby-on-rails

Im working to create a getting started controller, that guides a new users through uploading a photo, finding friends, inviting people etc.
GettingStarted has no model itself, it just guides users through a wizard. A user could fully bypass this gettingstarted process without breaking the site. It's just a guide...
What I've done so far is:
Create a Route, Controller and Model:
Route:
resources :getting_started
namespace :getting_started do
resource :users, :only => [:edit, :update]
end
Controller:
class GettingStartedController < ApplicationController
def index
#current_step = current_step
end
protected
def current_step
current_step || steps.first
return 1
end
def steps
%w[step1 step2 step3]
end
end
Model
class GettingStarted < ActiveRecord::Base
attr_writer :current_step
attr_accessor :current_step
def current_step
#current_step || steps.first
return 1
end
def steps
%w[step1 step2 step3]
end
def next_step
self.current_step = steps[steps.index(current_step)+1]
end
def previous_step
self.current_step = steps[steps.index(current_step)-1]
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
end
View:
<%= #current_step.inspect %>
<% form_for #gettingstarted do |f| %>
<table>
<tbody>
<tr>
<td>
<%= link_to image_tag current_user.profile_pic.url(:large), :class => 'getting-started-profile-pic' %>
</td>
<td>
Upload a photo
</td>
</tr>
<table>
<tbody>
<% end %>
Right now I'm stuck on the issue that I need GettingStarted to guide users through existing models, not be a model itself. And I'm getting undefined method `model_name' for NilClass:Class
Suggestions, thoughts on the above?
Thanks

your GettingStarted model doesn't have to inherit from ActiveRecord::Base but it does, you are getting error because Base is expecting a table in your db called GettingStarteds, or something. If you want to keep the content dynamic, meaning saving it in the db so you can change it, then you are pretty close, you could use a natural language model like 'steps' and the steps have an order associated with them, then you can look up the step based on its order in the getting started controller. you can also use a vanilla workflow with a steps controller, and then rename the route in the routes with the :as => option
if the steps are static you might want to explore some of the static page model libraries available like high voltage https://github.com/thoughtbot/high_voltage

Related

Creating multiple unrelated associated model instances in Rails

I have what I believe is fairly simple model setup:
class Booking < ApplicationRecord
has_many :payments
end
class Payment < ApplicationRecord
belongs_to :booking
end
Now, I want to create a form that allows a user to register payments in batch. That is, the form should have a number of input rows, each one representing a payment for some booking (i.e., each row has some fields for the columns of Payment plus a booking_id field). Upon submitting, each row should cause the creation of a corresponding Payment, which should be associated with the Booking indicated by the user for that row.
This seems to be surprisingly tricky, and my Google-Fu is failing me. I've tried the following (inspired by this post describing a solution without associations), which I thought would work, but which, well, doesn't:
class Admin::PaymentController < Admin::Controller
def batch
#payments = []
5.times do
#payments << Payment.new
end
end
def submit
params["payments"].each do |payment|
if payment["booking_id"] != "" || payment["amount"] != ""
Payment.create(payment_params(payment))
end
end
end
private
def payment_params(p)
p.permit(:booking_id, :amount)
end
end
<%= form_tag admin_payment_submit_path do %>
<% #payments.each do |payment| %>
<%= fields_for 'payments[]', payment do |p| %>
<%=p.text_field :booking_id%>
<%=p.number_field :amount%>
<% end %>
<% end %>
<%= submit_tag %>
<% end %>
This renders the form without erroring out, but the HTML names work out such that only a single payment (the last one) is submitted (e.g., name="payments[booking_id]"). Furthermore, upon submitting, I get the error
undefined method `permit' for "booking_id":String Did you mean? print
Which is less than helpful.
I've tried other variations too, but I feel like at this point I'm just feeling my way in the dark. Any guidance would be greatly appreciated!
params in controller is a instance of ActiveController::Parameter that has permit method.
But params["payments"] is a just array as subset of params.
For multiple payment params
def submit
payment_params.each do |payment|
if payment["booking_id"].present? || payment["amount"].present?
Payment.create(payment)
end
end
end
private
def payment_params
params.permit(payments: [:booking_id, :amount])["payments"]
end
For Single payment param
def submit
if payment_param["booking_id"].present? || payment_param["amount"].present?
Payment.create(payment_param)
end
end
private
def payment_param
params.require(:payments).permit(:a, :b)
end

Beginner #Rails: undefined method `title' for nil:NilClass and Couldn't find Decision with 'id'=edit

For your context: This is my first attempt to create a app. I have just started coding:-).
I am trying to get a simple CRUD setup to work.
Now i'm having two problems i can't get my head around:
My entries don't show up on my index page. it gives te following error: 'undefined method `title' for nil:NilClass'. The model contains the following columns:
string :title,text :forecast, date :review_date
If i go to decisions/edit it gives me the following error: 'Couldn't find Decision with 'id'=edit'
This is my code:
Controller:
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update]
def index
# gets all rows from decision table and puts it in #decision variable
#decisions = Decision.all
end
def show
# find only the decision entry that has the id defined in params[:id]
#decision = Decision.find(params["id"])
end
# shows the form for creating a entry
def new
#decision = Decision.new
end
# creates the entry
def create
#decision = Decision.new(decision_params)
if #decision.save
redirect_to #decision
else
render 'new'
end
end
# shows the form for editing a entry
def edit
#decision = Decision.find(params["id"])
end
# updates the entry
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find(params["id"])
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
index view
<h1>Hello World ^^</h1>
<% #decisions.each do |descision| %>
<p><%= #decision.title %></p>
<% end %>
routes.rb
Rails.application.routes.draw do
resources :decisions
root 'decisions#index'
end
I have been working on these two all morning but i can't figure it out. I would be a great help if you guys can take a look for me.
I have just started coding
Welcome!!
My entries don't show up on my index page.
I'm sure you mean decisions, right?
If so, you have to remember that if you're calling a loop in Ruby, you'll need some conditional logic to determine if it's actually populated with any data before trying to invoke it:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |decision| %>
<%= content_tag :p, decision.title %>
<% end %>
<% end %>
This will have to be matched by the appropriate controller code:
#app/controllers/decisions_controller.rb
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update, :destroy]
def index
#decisions = Decision.all
end
def show
end
def new
#decision = Decision.new
end
def create
#decision = Decision.new decision_params
#decision.save ? redirect_to(#decision) : render('new')
end
def edit
end
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find params["id"]
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
This will give you the ability to call #decisions and #decision in your views depending on which route you're accessing.
An important point is that when you say...
decisions/edit it gives me the following error: Couldn't find Decision with 'id'=edit'
... the issue is caused by the way in which Rails routing is handled:
Because Ruby/Rails is object orientated, each set of routes corresponds to either a collection of objects, or a member object. This is why routes such as edit require an "id" to be passed - they're designed to work on member objects.
As such, when you access any "member" route (decisions/:id, decisions/:id/edit), you'll have to provide an id so that Rails can pull the appropriate record from the db:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |descision| %>
<%= link_to "Edit #{decision.title}", decision_edit_path(decision) %>
<% end %>
<% end %>
I can explain a lot more - the above should work for you for now.

Rails: How to only allow User to apply to job only once?

I am creating a job board, and I don't want to allow the users the option to apply for the same job twice. How can I limit this?
app/views/jobs/job.html.erb
<% if applied_to_this_job? %>
<div class="alert" role="alert">You have already applied to this job!</div>
<% else %>
<%= link_to 'Apply', new_job_application_path(#job) %>
<% end %>
app/helpers/jobs_helper.rb
def applied_to_this_job?
JobApplication.exists? user_id: current_user.id
end
Obviously this doesn't work because it checks if this user has applied to any job. How Can I check to see if the current user has applied to the job being viewed.
Also, how can I limit this at the controller level so that the user can't go to job_application/new and get to the form.
You would use a before_filter in the controller action.
class JobsController < ActionController::Base
before_filter :has_applied?, only: [new, create]
....
private
def has_applied?
if JobApplication.where(user_id: :current_user.id, job_id: params[:job_id]).any?
redirect_to :index, alert: "You have already applied"
end
end
end
This would allow the user to visit /jobs/new and post the application to /jobs/create unless they have applied. If they have applied, they will be redirected to the index in the sample code.
Also as another answer has noted, it would be wise to pass in the job id as well. Updated sample code above to reflect.
You need to check and see if the JobApplication object is for this #job try:
JobApplication.where( user_id: current_user.id, job_id: #job.id ).exists?
Although what you've accepted will work, I think it's somewhat of a surface-level fix.
You'll be much better using validators to determine if the user can actually create another job application. This will protect against any problems with the business logic in your "front-end" views
Here's how I'd handle it:
--
Uniq
#app/models/user.rb
class User < ActiveRecord::Base
has_one :job_application
end
#app/models/job_application.rb
class JobApplication < ActiveRecord::Base
belongs_to :user
validates :user_id, uniquness: true
end
You may also wish to give your database a uniq index for your user_id column:
> $ rails g migration AddUniqueIndex
#config/db/add_unique_index.rb
class AddUniqueIndex < ActiveRecord::Migration
def change
add_index :job_applications, [:job_id, :user_id], unique: true
end
end
This will give you a highly efficient DB-level uniqueness index - meaning that if you try and add any more applications than is permitted, it will either fail silently, or come back with an error.
Controller
The structure of the controller would allow you to be less stringent about the accessibility of the job_application functionality:
#app/views/jobs/job.html.erb
<% if current_user.has_applied?(params[:job_id]) %>
<div class="alert" role="alert">You have already applied to this job!</div>
<% else %>
<%= link_to 'Apply', new_job_application_path(#job) %>
<% end %>
#app/models/user.rb
class User < ActiveRecord::Base
has_many :job_applications
def has_applied?(job_id)
job_applications.find job_id
end
end

Ruby on Rails -- Navigation Properties :o?

I'm new to Rails and I have two models (Ticket, User).
One Ticket has one user - One User has_many tickets.
I would like to display "the username of a ticket" on an overview page..
I already set my models and assoziations. The following code should show a 3-column table with the username in the middle, but I get an error :(
"undefined method `users' for ..."
I'm coming from C# using Entity Framework.. I thought RoR does understand the expression "article.user.firstname"
<% #tickets.each do |article| %>
<tr>
<td class="cell"><%= article.title %></td> //fine
<td class="cell"><%= article.user.firstname %></td> // le error :(
<td class="cell"><%= article.description %></td> //fine
</tr>
<% end %>
Could someone show me how to archive this ?
User.rb
class User < ActiveRecord::Base
has_many :tickets, dependent: :destroy
end
Ticket.rb
class Ticket < ActiveRecord::Base
belongs_to :user
end
ticket_controller.rb
class TicketsController < ApplicationController
def index
#tickets = Ticket.all.includes(:user)
end
def new
#users = User.all
end
def create
#ticket = Ticket.new(article_params)
#ticket.save
redirect_to #ticket
end
def show
#ticket = Ticket.find(params[:id])
end
private
def article_params
params.require(:ticket).permit(:title, :description)
end
end
UPDATE:
I had to redefine my model with "rails generate model Ticket title:string message:string user_id".
After that I ran "rake db:migrate" (after deleting that table, otherwise I got an error).
Then I set assoziations by writing "belongs_to :user" into ticket.rb and "has_many :tickets" into my user.rb-file.
Can you post your user table, ticket table and ticket controller.
to see if you have a syntax error.
You say that tickets only have 1 user and you have article.users and not article.user
add user to the
def article_params
params.require(:ticket).permit(:title, :description,:user_id)
end
Can you post the form to use if your getting the current user id on the form
on the migration of the ticket table
do you have a
t.integer :user_id
The problem I'm noticing is that when you create the list the user is looking for is at this table tickets and not to Users table.That's why you do not give error when using only article.user
on the Console try User.all and see if you got user and then try the same with Ticket.all
In the comments, you mention you're getting an error along the lines of "undefined method `firstname' for nil:NilClass". This means that your ticket has no user assigned, so the user is nil. You're getting this error because you're trying to treat nil like a User.

Displaying Associated Objects in Rails Views

I am working on a rails 4 application that currently has two models User and Status. In the user model I defined the association below. Both the status and user tables are populating with information. Statuses are loading with an associated user_id
User Model
class Status < ActiveRecord::Base
belongs_to :user
end
I have the following block in my show status view which will display the user_id and and the content of the status
<% #statuses.each do |status| %>
<div class="status">
<strong> <%=status.user_id%></strong>
<p> <%=status.content%></p>
I would like to display the user's first name instead. According the tutorial i'm taking I should be able to use this code since I have the association defined however it's returning the error below.
<%=#status.user.first_name%>
Error
#==>undefined method `first_name' for nil:NilClass
How can I display first_name in the controller? Do I need to define a new method for user or should the association provide?
Relevant Controller Code for Reference
class StatusesController < ApplicationController
before_action :set_status,:set_user, only: [:show, :edit, :update, :destroy]
# GET /statuses
# GET /statuses.json
def index
#statuses = Status.all
end
# GET /statuses/1
# GET /statuses/1.json
def show
puts "debug msg #{#status.inspect}"
end
# GET /statuses/new
def new
#status = Status.new
end
# GET /statuses/1/edit
def edit
end
# POST /statuses
# POST /statuses.json
...
...
...
private
# Use callbacks to share common setup or constraints between actions.
def set_status
#status = Status.find(params[:id])
puts "in set status"
end
def set_user
#status.user = User.find_by(#status.user_id)
end
# Never trust parameters from the scary internet, only allow the white list through.
def status_params
params.require(:status).permit(:content, :user_id)
end
end
Sees like there is no problem in your code. The error undefined method first_name for nil:NilClass means that the status object not associated with user or user have no field first_name. Try following code:
<% #statuses.each do |status| %>
<div class="status">
<strong> <%=status.user.try(:first_name) %></strong>
<p> <%=status.content%></p>
I am not sure what page you are trying to display <%=#status.user.first_name%> this on, but this should work.
You can use the will_paginate gem:
def show
#statuses = #statuses.paginate(page: params[:page])
end
add this to the view:
<%= will_paginate %>
or this should be the normal way:
def show
#statuses = #statuses.find(params[:id])
end

Resources