I am creating a simple workflow where, after signing up, a publisher can create a newsletter. This newsletter needs three pieces of information: title, description, and publisher_id (i.e. the creator). My question is two-fold:
What is the 'correct' way to set the publisher_id, given that newsletters will have posts nested inside them and Rails recommends not nesting resources more than one level deep (i.e. I shouldn't nest newsletter inside publisher)?
If I am generally approaching it the correct way (see below), how am I supposed to pass the publisher_id and what am I doing wrong?
The workflow is as follows:
Create publisher and set session[:id] to #publisher.id.
Redirect to the newsletter new view.
Upon creating a newsletter, set the newsletter's publisher_id to the session[:id].
Upon navigating to to '/newsletters/new', I'm seeing the following error:
Started GET "/newsletters/new" for ::1 at 2020-05-04 15:53:22 -0700
Processing by NewslettersController#new as HTML
"<ActionController::Parameters {\"controller\"=>\"newsletters\", \"action\"=>\"new\"} permitted: false>"
Rendering newsletters/new.html.erb within layouts/application
Rendered newsletters/new.html.erb within layouts/application (Duration: 2.3ms | Allocations: 738)
And upon submitting 'Create Newsletter', I'm seeing the following error:
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/newsletters_controller.rb:21:in `create'
Started POST "/newsletters" for ::1 at 2020-05-04 15:58:34 -0700
(0.0ms) SELECT sqlite_version(*)
Processing by NewslettersController#create as JS
Parameters: {"authenticity_token"=>"XXX", "newsletter"=>{"title"=>"Newsletter 1", "description"=>"Description content"}, "commit"=>"Create Newsletter"}
Completed 500 Internal Server Error in 11ms (ActiveRecord: 1.0ms | Allocations: 7085)
publishers_controller.rb
class PublishersController < ApplicationController
def create
#publisher = Publisher.new(publisher_params)
if #publisher.save!
session[:id] = #publisher.id
redirect_to new_newsletter_path
else
render 'new'
end
end
private
def publisher_params
params.require(:publisher).permit(:email, :password)
end
end
newsletters_controller.rb
class NewslettersController < ApplicationController
def new
#newsletter = Newsletter.new
end
def create
#newsletter = Newsletter.new(newsletter_params)
if #newsletter.save!
redirect_to #newsletter
else
render 'new'
end
end
private
def newsletter_params
params.require(:newsletter).permit(:title, :description).merge(publisher_id: session[:id])
end
end
/newsletters/new.html.erb
<%= form_with model: #newsletter, url: newsletters_path do |form| %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :description %><br>
<%= form.text_area :description %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
You have misunderstood what the rails guides meant by "nesting resources more than one level deep" - whats really meant is that this is OK:
/publishers/1/newsletters/new
Which is one level of nesting and the nesting provides very useful contextual information. While these are kind of fishy:
/publishers/1/newsletters/2
/publishers/1/newsletters/3/replies/new
In both cases we have two levels of nesting should be able to reach the nested resource without going though publishers.
/newsletters/2
/newsletters/3/replies/new
Also if you want to add values from the session or somewhere else then the params hash when creating a record use a block or create the record off the association instead:
class NewslettersController < ApplicationController
def create
#newsletter = Newsletter.new(newsletter_params) do |newletter|
newsletter.publisher = current_publisher
end
# or
# #newsletter = current_publisher.newsletters(newsletter_params)
# save! will raise an exception if the record is not valid
# that is NOT what you want here
if #newsletter.save
redirect_to #newsletter
else
render 'new'
end
end
end
This makes it much more apparent what is coming from where.
Related
I'm running into a perplexing issue that I can only resolve partway, and hopefully, someone more experienced can tell me whether I can achieve what I wish, or if I'm barking up the wrong tree.
I have a Rails 4 application which uses Devise and CanCan. I'd like to make a small subset of application functionality available to guest users (not logged in). I can achieve this by specifying a get route to a controller method and using link_to to reach that method. I cannot, however, figure out how to get the value of a select box to pass along as parameters on that page without making that view a form using form_tag (there is no model associated with this view).
I can pass hardcoded params along like so:
<%= link_to "Month", activities_search_month_path(:resource_id => 4) %>
but I'd rather have something like:
<%= link_to "Month", activities_search_month_path(:foo => :resource_id) %>
where the second symbol refers to the value of a select_tag. This second example delivers a literal value of "resource_id" when I dump the :foo key unless I convert my view to a form.
If I turn the view into a form by enclosing all the erb in a form_tag, I get a 401 Forbidden error, after which the Devise sign in form is rendered. My guess is that any time you want to process a form, Rails (or Devise) demands authentication on some level. The behavior is the same when I use button_to rather than link_to, since button_to wraps itself in a form under the covers.
How can I set that resource_id argument in my link_to, or will I be forced to create a guest user access level and silently log in guest users? It's important for the UX that users can access this functionality with the least amount of effort possible.
Thanks in advance.
Addendum: quick_search method from controller
def quick_search
puts "quick search 0"
if(params[:time_period] == 'today')
#resource = Resource.find(params[:resource_id])
#site = Site.find(params[:site_id])
#time_period_string = "Activities for #{localize_date(Date.today)} at #{#resource.name}, #{#site.name}"
puts "quick search 1"
if user_signed_in?
puts "quick search 2a"
#activities = Activity.where("system_id = ? and start_date = ? and activity_status_id = ? and resource_id = ?", current_system_id, #today, 2, params[:resource_id])
else
puts "quick search 2b"
if(Setting["#{current_subdomain_not_signed_in}.quick_search_guest_access"] == 'true')
puts "quick search 3a"
current_system_id = current_system_id_not_signed_in
#activities = Activity.where("system_id = ? and start_date = ? and activity_status_id = ? and resource_id = ?", current_system_id, #today, 2, params[:resource_id])
else
puts "quick search 3b"
redirect_to '/users/sign_in'
end
end
end
Note: the quick_search method is never entered. CanCan (or maybe Devise) steps in immediately and redirects to sign in:
Console output:
Started GET "/activities/quick_search" for 127.0.0.1 at 2015-04-12 18:01:58 -0700
Processing by ActivitiesController#quick_search as HTML
(0.2ms) SELECT DISTINCT "systems"."subdomain" FROM "systems"
Completed 401 Unauthorized in 1ms
Started GET "/users/sign_in" for 127.0.0.1 at 2015-04-12 18:01:58 -0700
Processing by Devise::SessionsController#new as HTML
(0.2ms) SELECT DISTINCT "systems"."subdomain" FROM "systems"
Rendered layouts/_header.html.erb (0.8ms)
Rendered devise/shared/_links.html.erb (4.1ms)
Rendered devise/sessions/new.html.erb within layouts/application (14.7ms)
Rendered layouts/_footer.html.erb (0.0ms)
Completed 200 OK in 285ms (Views: 282.3ms | ActiveRecord: 0.2ms)
Ability.rb
can :quick_search, Activity
can :search_day, Activity
can :search_week, Activity
can :search_month, Activity
The odd thing is that link_to quick_search fails with a 401, but link_to the other three methods works fine -- I just can't get parameters to them dynamically.
If you are using CanCan(Can?) you can define a special ability for guests.
How does your Ability-model look?
Which controller are handling the action that you want to view?
How do you authenticate with CanCan in this controller?
https://github.com/CanCanCommunity/cancancan/wiki/CanCan-2.0
Under the "Defining Abilities" you can see a non-user example.
Fixing CanCan is probably the best option, if you do not want to:
For the part with the link and select box it would be easiest to handle as a form and then handle the redirect in the controller, it could also be done with a remote ajax form.
http://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html
This should work:
<% form_tag Activity, activity_quick_search_path, remote: true do %>
<%= select_tag :resource_id...%>
<%= submit_tag %>
<%end%>
Edit after comments:
The culprit here is(was) an:
before_action :authenticate_user!
Causing Devise to redirect to sign in page.
However, if you have CanCan you shouldn't need the authenticate_user.
Short example:
With only Devise I would do:
class NewsController < ApplicationController
before_action :authenticate_user!
before_action :set_news, except: [ :index, :new ]
def index
#news = News.all
end
def show
end
def new
#news = News.new
end
def edit
end
def create
#news = News.new(news_params)
flash[:notice] = 'News created' if #news.save!
redirect_to #news
end
def update
#news.update! news_params
redirect_to #news
end
def destroy
#news.destroy!
redirect_to News
end
private
def news_params
params.require(:news).permit(some_attributes)
end
def set_news
#news = News.find(params[:id])
end
end
How it looks with CanCanCan:
class NewsController < ApplicationController
load_and_authorize_resource
def index
end
def show
end
def new
end
def edit
end
def create
flash[:notice] = 'News created' if #news.save!
redirect_to #news
end
def update
#news.update! news_params
redirect_to #news
end
def destroy
#news.destroy!
redirect_to News
end
private
def news_params
params.require(:news).permit(some_attributes)
end
end
Which I find super neat 😄
Hope that this can help as well.
I'm building a simple API with Rails 4, but with my "create" method, it all goes horribly wrong.
Here is the relevant part of my routes file:
namespace :api, defaults: { format: 'json' } do
# /api/... Api::
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users
end
end
Here is the api/v1/users_controller.rb:
class Api::V1::UsersController < ApplicationController
protect_from_forgery except: :create
respond_to :json
def index
respond_to do |format|
format.html {render text: "Your data was sucessfully loaded. Thanks"}
format.json { render text: User.last.to_json }
end
end
def show
respond_with User.find(params[:id])
end
def create
respond_with User.create(user_params)
end
def update
respond_with User.update(params[:id], params[:users])
end
def destroy
respond_with User.destroy(params[:id])
end
private
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => [])
end
end
Whenever I try to add an API with JSON, I get "{"errors":{"name":["can't be blank"]}}"
It works to create a user with my regular controller, but I have a feeling my API controller is getting messed up because of the Strong Parameters.
Any suggestions for how to do this correctly in Rails 4?
Also, I have a few Has-Many-Through relationships through my user model. The API's user controller should be able to see that off the bat, right?
Thanks
EDIT:
I'm now getting this error:
EDIT:
{
"name": "Sally",
"age": "23",
"location": "Blue York",
"genre_ids": [1, 2, 3]
}
EDIT AGAIN
Even with adding the User parameter in my JSON call, it still gives me the same error of the :user param missing. Am I using strong parameters incorrectly? In my "regular" users_controller, I can create a user easily with a form that I have set up, but with this API controller, I can't seem to create one with JSON. Any other suggestions?
EDIT YET AGAIN
Here Is The Log From Start to Error
rails s
=> Booting WEBrick
=> Rails 4.0.1 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2013-12-19 14:03:01] INFO WEBrick 1.3.1
[2013-12-19 14:03:01] INFO ruby 1.9.3 (2013-02-22) [x86_64-darwin10.8.0]
[2013-12-19 14:03:01] INFO WEBrick::HTTPServer#start: pid=53778 port=3000
Started GET "/api/users" for 127.0.0.1 at 2013-12-19 14:03:02 -0500
ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by Api::V1::UsersController#index as JSON
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Rendered text template (0.0ms)
Completed 200 OK in 142ms (Views: 27.8ms | ActiveRecord: 0.6ms)
[2013-12-19 14:03:03] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-12-19 14:03:03] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
Started POST "/api/users" for 127.0.0.1 at 2013-12-19 14:03:37 -0500
Processing by Api::V1::UsersController#create as JSON
Completed 400 Bad Request in 1ms
ActionController::ParameterMissing (param not found: user):
app/controllers/api/v1/users_controller.rb:40:in `user_params'
app/controllers/api/v1/users_controller.rb:20:in `create'
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack- 4.0.1/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.7ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-4.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-4.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.8ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack- 4.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (31.6ms)
EDIT #6
Here is my "real" users_controller that lives in my app and not my API. The form creates a user from this controller and NOT the API controller.
class UsersController < ApplicationController
def index
#users = User.all
#genres = Genre.all
#instruments = Instrument.all
render json: #users
end
def new
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
render json: #user, status: :created, location: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => [])
end
end
ALSO - The User Form
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :age %>
<%= f.text_field :age %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :location %>
<%= f.text_field :location %>
<br>
<% Genre.all.each do |genre| %>
<%= check_box_tag "user[genre_ids][]", genre.id %>
<%= genre.name %><br>
<% end %>
<br>
<% Instrument.all.each do |instrument| %>
<%= check_box_tag "user[instrument_ids][]", instrument.id %>
<%= instrument.name %><br>
<% end %>
<%= f.submit "Create My Account!" %>
<% end %>
</div>
</div>
<%= users_path %>
Here is my user.rb File
class User < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 50 }
has_many :generalizations
has_many :genres, through: :generalizations
has_many :instrumentations
has_many :instruments, through: :instrumentations
end
Here is what I have in my routes file:
namespace :api do
namespace :v1 do
resources :users
end
end
My POST Request
POST /api/v1/users HTTP/1.1
Host: localhost:3000
Cache-Control: no-cache
{ "user": { "name": "Sally", "age": "23", "location": "Blue York", "genre_ids": [1, 2, 3] } }
UPDATE
I changed my strong-params to be this:
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => []) if params[:user]
end
So the "if" statement at the end makes the error go away, but whenever I post to my API, it gives me back "null". So this could be the same problem as before, but shown in a different way. But, at the same time, it could be progress!
Here Is The Log For The Previous Update
Started POST "/api/v1/users" for 127.0.0.1 at 2013-12-21 11:38:03 -0500
Processing by API::V1::UsersController#create as */*
(0.1ms) begin transaction
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
(0.1ms) rollback transaction
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Rendered text template (0.0ms)
Completed 200 OK in 20ms (Views: 0.3ms | ActiveRecord: 0.6ms)
FINAL UPDATE
I was missing a few things, but the main thing that did it was that I was missing "Content-Type - application/json" as my Header.
I feel so accomplished! Thanks for all your help, everyone!
According to your code parameters in the JSON you are posting should be inside params[:user]. So the JSON should look like:
{
"user": {
"name": "Sally",
"age": "23",
"location": "Blue York",
"genre_ids": [1, 2, 3]
}
}
Rails 4 is a great choice for building APIs. I would go with the rails-api gem. It will perform way better than a full blown Rails stack.
I have built plenty of API's in Rails using the Rails API gem. Usually in combination with RABL (which you can use to create nice templates to render your JSON). I am not a big fan of integrating an API directly into your production Rails app (serving websites and JSON) as you will create a big mess over time when starting to add more versions to your API. There are some very good Railscasts (www.railscasts.com): Search for API.
When accessing your API you would use a global filter in your application_controller.rb file. You could do something like this:
before_filter :authenticate_user, :except => 'users#index'
private
def authenticate_user
#current_user = User.find_by_api_key(params[:token])
unless #current_user
render :status=>403, :json=>{:message=>"Invalid token"}
end
end
def current_user
#current_user
end
end
In this case you would send the token in your request (that's quick and dirty, rather use the header instead) as a request parameter. You need to add the API key or whatever you want to use to your user model. Just create a migration adding api_key or however you want to call it to the user model or create a new table with keys, secrets etc. and a user_id field for your belongs_to (and a User has_many api_keys, or has_one). This way you can allow your users at any time to change their keys etc. (re-generate) without messing with usernames/password or even allow them to have multiple API keys with some tags (testing, production, etc). For your user signup you could add to your model:
before_create :generate_api_key
and then create a simple method like:
def generate_api_key
begin
self.api_key = SecureRandom.hex
end while self.class.exists?(api_key: api_key)
end
Hope it helps!
I'm on Rails 4. Let's say I have three models: House, Color, and HouseColoring.
class House < ActiveRecord::Base
has_many :house_colorings
has_many :colors, through: :house_colorings
accepts_nested_attributes_for :house_colorings, allow_destroy: true
end
class Color < ActiveRecord::Base
has_many :house_colorings
has_many :houses, through: :house_colorings
end
class HouseColoring < ActiveRecord::Base
belongs_to :house
belongs_to :color
end
houses_controller.rb:
class HousesController < ApplicationController
before_action :set_house
...
def new
#house = House.new
#house.house_colorings.build
end
def create
#house = House.create(house_params)
if #house.save
redirect_to #house
else
render 'new'
end
end
def edit
#Gets #house from set_house
end
def update
if #house.update(house_params)
redirect_to #house
else
render 'edit'
end
end
...
private
def set_house
#house = House.find(params[:id])
end
def house_params
params.require(:house).permit(:some_parameters, house_colorings_attributes: [:id, :color_id])
end
end
Here is my _form.html.erb partial for my house new and edit
<%= form_for #house do |f| %>
<div id="house_colorings">
<%= f.fields_for :house_colorings do |c| %>
<%= render "house_colorings", f: c %>
<% end %>
<%= link_to "Add color", add_color_path, remote: true %>
</div>
<% end %>
_house_colorings.html.erb:
<%= f.collection_select :color_id, Color.all, :id, :name, {include_blank: "Select color"} %>
In the houses_controller, I've added:
def add_color
respond_to do |format|
format.js
end
end
add_color.js.erb:
$("#house_colorings").append("<%= escape_javascript render 'house_colorings', f: c %>");
I added a route for my add_color method:
GET "/add_color" => "houses#add_color"
When I click my add color link, nothing happens on screen, but in my log I get a 500 internal server error.
Started GET "/add_color" for 127.0.0.1 at 2013-10-26 21:11:41 -0700
Processing by HousesController#add_color as JS
Rendered houses/add_color.js.erb (11.3ms)
Completed 500 Internal Server Error in 14ms
ActionView::Template::Error (undefined local variable or method `f' for #<#<Class:0x007fc317428538>:0x007fc31710d060>):
1: $("#house_colorings").append("<%= escape_javascript render 'house_colorings', f: c %>");
app/views/houses/add_color.js.erb:1:in `_app_views_houses_add_color_js_erb__1847085463095078116_70237941180700'
As of now, I only have one field to add a house_coloring to my house. I want to add some ajax, and have a link in my form that adds a new field after the one that is there, but I'm not sure how to do this.
I've gone through the "Nested model form" from Railscasts and used parts of them to get to the point I am now, but I would like to use the "data_remote" helpers provided by rails if I can. I've edited my question and included the log for the error I'm getting when I click my add color link. I'm pretty sure that I need to change either my add_color.js.erb or the add_color action in my houses controller.
Any suggestions?
Well, you do have couple alternatives here.
Use the information in the Nested model forms railscasts: Part 1 and Part 2
Use the FormObject Pattern to make thet nesting a bit easier. The pattern is described in a dozen places and also on railscasts (subscribtion needed).
Use a js framework like Angular.js to add new fields on the client side on the fly. Angular.js is also covered in a railscast (subscribtion needed) and has very rich documentation.
UPDATE
The error tells you pretty much all of it. You send a c object to the partial as a form-builder object. And seems like you don't instantiate it in the houses#add_color action.
Check out these two railscasts episode:
Nested Forms 1
Nested Forms 2
The 2nd one explains in depth what you're exactly looking for.
sorry I am new to rails
Right now I am trying to build a small application just like https://pinboard.in, I am trying to get a summer internship with them.
Here is my Bookmark model
class Bookmark < ActiveRecord::Base
attr_accessible :url, :title, :description, :counter
belongs_to :user
#validates that url has https:// or http://
validates :url, :format => { :with => /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0- 9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix,
:message => "Invalid URL" }
end
Here is my bookmark controller
class BookmarksController < ApplicationController
def add_bookmark
#bookmark = Bookmark.new
respond_to do |format|
format.html
end
end
def draw_recent
#bookmarks = Bookmark.all
end
end
Here is my form
<%= form_for :bookmark do |f| %>
URL: <%= f.text_field :url %><br/>
Title: <%= f.text_field :title %><br/>
Description: <%= f.text_field :description %><br/>
<%= f.submit "Submit" %>
<% end %>
Everything get rendered correctly and when I put in the information and submit add
here is my output
Started POST "/add" for 127.0.0.1 at 2013-05-09 09:55:58 -0400
Processing by BookmarksController#add_bookmark as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ZCxA226pOVyu5AkQAmvvfKz5uPQ4gFggPTwrswtqZYk=", "bookmark"=> {"url"=>"http://litmus.com", "title"=>"Email marketing ", "description"=>"email marketing "}, "commit"=>"Submit"}
Rendered bookmarks/_form.html.erb (1.9ms)
Rendered bookmarks/add_bookmark.html.erb within layouts/application (3.3ms)
Completed 200 OK in 96ms (Views: 95.4ms | ActiveRecord: 0.0ms)
I have two thoughts in my head, well my form is posting it correctly but somehow it is not saving anything to my db, do I need a save method in my controller?
when I try #bookmark instead of :bookmark the app throws me an error saying wrong bookmarks.path
undefined method `bookmarks_path
I understand the former you are actually working with the instance #bookmark in the controller and the latter is wrapping around the model....
Can someone enlighten me? i feel like this is very trivial for you guys... :)
Should be form_for #bookmark do |f|. You are getting the undefined method error because you have not defined your routes in config/routes.rb, add there:
resources :bookmarks
That will add the RESTful resources following the CRUD convention, so you should change the name of your controller methods for this to work out-of-the-box. You will need at least three methods for what you are doing right now:
In your app/controllers/bookmarks_controller.rb:
First one will render your form at /bookmarks/new
def new
#bookmark = Bookmark.new
end
Second one will process the form submission (no action is needed from you apart that following the naming convention)
def create
#bookmark = Bookmark.new(params[:bookmark])
#bookmark.save
end
Third one to show the bookmarks as in your current 'draw_recent'
def index
#bookmark = Bookmark.all
end
Afterwards you can go on validating the data, etc but the basic flow should be like that.
It is better to start with the conventions to go later on to change the method standard names when you have more confidence.
Ok ill be honest, i haven't spent much time looking for a solution yet seeing as how my son is keeping my attention running around. Either way I would like to ask a question for something that seems pretty simple but has stumped me thus far.
So to keep it simple lets say I have Users(w/model) and Home controllers, Home is the root route.
In the root directory I want to be able to see all posts made by the User using ajax to update a partial on the home page with the list of posts.
In the users controller I have a def called userposts with this in it
def userposts
#user = User.find_by_id(params[:id])
#userposts = #user.posts.all(:order => "created_at DESC")
respond_to do |format|
format.js { #userposts}
end
end
And in my view I have
<p id="aboutuser">
<% if #user.about? %>
<%= " " + #user.id.to_s %>
<% else %>
User has not yet filled this out.
<% end %>
</p>
<h3 id="authpostlink">
<%= link_to "List of all posts", user_userposts_path(#user.id), :id => #user.id, :remote => true %>
</h3>
my errors are as follows
Started GET "/users/2/userposts" for 127.0.0.1 at Sun Jan 15 13:36:23
-0600 2012 Processing by UsersController#userposts as JS Parameters: {"user_id"=>"2"} User Load (0.1ms) SELECT "users".*
FROM "users" WHERE "users"."id" IS NULL LIMIT 1 Completed 500 Internal
Server Error in 1ms
NoMethodError (undefined method posts' for nil:NilClass):
app/controllers/users_controller.rb:27:inuserposts'
Rendered
/home/n0de/.rvm/gems/ree-1.8.7-2011.03/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb
(0.8ms) Rendered
/home/n0de/.rvm/gems/ree-1.8.7-2011.03/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
(0.8ms) Rendered
/home/n0de/.rvm/gems/ree-1.8.7-2011.03/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (3.2ms)
I do realize i did not post the _show.js.erb file that calls the action to update the div but according to the error messages it doesn't seem the process has gotten that far.
Assuming you have the following:
# /app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
end
# /app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
I would add a nested resource in your routes file:
#/config/routes.rb
resources :users do
resources: posts
end
You get a bunch of great "_path" methods for free (run $ rake routes from your console to see them all), and it gives you access to URLs such as /users/123/posts. This request will go to the index method of your PostsController and will automatically include :user_id => 123 in the params hash. You can then do the following:
# In your view:
<%= link_to "List of all posts", user_posts_path(#user), :remote => true %>
<div id="posts"></div>
# /app/controllers/posts_controller.rb
class PostsController < ApplicationController
respond_to :js # allows for AJAX requests
def index
if params[:user_id].present? # Do this if using the nested resource
#user = User.find(params[:user_id])
#posts = #user.posts.order('posts.created_at DESC')
else # Otherwise, treat it like a normal request
#posts = Post.all
end
respond_with #posts
end
end
Because the your request is sent remotely, you need a corresponding "js" version of your index view (note the file name below and see this Railscast for more explanation):
# /app/views/posts/index.js.erb
$('#posts').html("<%= escape_javascript(render(#posts)) %>");
This will render out the posts into that <div id="posts"> tag. (You'll probably need a "_post.html.erb" partial in /app/views/posts/" as well.)
However, having said all this, are you sure you need to do this via AJAX? You could simply preload all the posts in the UsersController#show method, initially hide the list using CSS, and then add a jQuery toggle() method on that link. Anyway, hope this makes sense and is helpful.