ruby on rails app tutorial can't display micropost page - ruby-on-rails-3

Hi I'm following a rails tutorial and i've kind of veered off the tutorial to do some different things...& i hit a snag. So the tutorial (michael hartl's) creates a feed of microposts, but it doesn't give each micropost its own page. This is what i'm trying to do and can't seem to get it working.
So in the feed view which i'm calling "activity" i've got:
<li id="<%= activity_item.id %>">
<%= link_to gravatar_for(activity_item.user, :size => 200), activity_item.user %><br clear="all">
<span class="title"><%= link_to activity_item.title, #micropost %></span><br clear="all">
<span class="user">
Joined by <%= link_to activity_item.user.name, activity_item.user %>
</span><br clear="all">
<span class="timestamp">
<%= time_ago_in_words(activity_item.created_at) %> ago.
</span>
<% if current_user?(activity_item.user) %>
<%= link_to "delete", activity_item, :method => :delete,
:confirm => "Are you sure?",
:title => activity_item.content %>
<% end %>
</li>
And when i click on the actual micropost "title" i get the following error saying i've got "No route matches [GET] "/microposts". I'm sure this is probably an easy fix i'm missing, but I'm a beginner & I've been goin too long & my brain is fried.
What i basically need to have happen is when i click on the title of a micropost from my activity feed...I need it to go to the unique #show page of that micropost id.
Here are the models / controllers i believe are relevant. If i need to post anything else just let me know. I appreciate any and all help! Thanks!
static_pages_controller (the home page is where my activity feed shows up
class StaticPagesController < ApplicationController
def home
if signed_in?
#micropost = current_user.microposts.build
#activity_items = current_user.activity.paginate(:page => params[:page])
#friendactivity_items = current_user.friendactivity.paginate(:page => params[:page])
end
end
def help
end
def about
end
def contact
end
end
Microposts Controller
class MicropostsController < ApplicationController
before_filter :signed_in_user, :only => [:create, :destroy]
before_filter :correct_user, :only => :destroy
def index
end
def new
#micropost = current_user.microposts.build if signed_in?
end
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_path
else
#activity_items = [ ]
render 'new'
end
end
def show
#micropost = Micropost.find(params[:id])
#users = #micropost.users.paginate(:page => params[:page])
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
end
Micropost Model
class Micropost < ActiveRecord::Base
attr_accessible :content, :title
belongs_to :user
validates :title, :presence => true, :length => { :maximum => 100 }
validates :content, :presence => true, :length => { :maximum => 220 }
validates :user_id, :presence => true
default_scope :order => 'microposts.created_at DESC'
# Returns Microposts from the users that the given user follows
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids})",
:user_id => user.id)
end
end
I'm also adding now the routes.rb file and micropost model as requested
routes.rb
SampleApp::Application.routes.draw do
resources :users do
member do
get :following, :followers
end
end
resources :sessions, :only => [:new, :create, :destroy]
resources :microposts, :only => [:create, :destroy, :show]
resources :relationships, :only => [:create, :destroy]
# home page route
root :to => 'static_pages#home'
# signup route
match '/signup', :to => 'users#new'
match '/signin', :to => 'sessions#new'
match '/signout', :to => 'sessions#destroy', :via => :delete
# static pages routes
match '/help', :to => 'static_pages#help'
match '/about', :to => 'static_pages#about'
match '/contact', :to => 'static_pages#contact'
# create a micropost routes
match '/createamicropost', :to => 'microposts#new'
microposts Model as requested...Thanks!
class Micropost < ActiveRecord::Base
attr_accessible :content, :title
belongs_to :user
validates :title, :presence => true, :length => { :maximum => 100 }
validates :content, :presence => true, :length => { :maximum => 220 }
validates :user_id, :presence => true
default_scope :order => 'microposts.created_at DESC'
# Returns Microposts from the users that the given user follows
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids})",
:user_id => user.id)
end
end

You are getting a routes error - No route matches [GET] "/microposts.
So, it's not related with code as such. Just declare the routes to microposts like this.
config/routes.rb
resources :microposts

Related

Rails 3 Devise customization

I have an app that uses 2 type of roles: users and admins. For the users I have overwritten both the RegistrationController and PasswordsController but after adding the PasswordsController the login page for the admin(different from the user, which is custom) gives the following routing error:
Routing Error
No route matches {:action=>"create", :controller=>"password_resets", :locale=>:admin}
this is my routes.rb file:
AppName::Application.routes.draw do
devise_for :admin
namespace :backend do
root :to => "home#index"
resources :exchanges, :except => :show
resources :prices, :except => :show
resources :offers
resources :newss
resources :users, :except => :show
scope ':locale', :locale => /en|ro/ do
namespace :profile do
root :to => 'home#index'
resources :orders do
resources :items
end
end
devise_for :users, :skip => [:sessions, :registrations, :passwords]
devise_scope :user do
get 'logare' => 'registrations#logare', :as => :new_user_session
post 'logare' => 'devise/sessions#create', :as => :user_session
delete 'index' => 'devise/sessions#destroy', :as => :destroy_user_session
get 'inregistrare' => 'registrations#new', :as => :new_user_registration
post 'inregistrare' => 'registrations#create', :as => :user_registration
get 'get_city_list' => 'registrations#get_city_list', :as => :get_city_list
get 'edit' => 'registrations#edit', :as => :user_edit
post 'edit' => 'registrations#update', :as => :user_update
get 'new_password' => 'password_resets#new', :as => :password_reset
post 'new_password' => 'password_resets#create', :as => :new_password
get 'edit_password' => 'password_resets#edit', :as => :edit_password_reset
post 'edit_password' => 'password_resets#update', :as => :update_password
end
end
match '/:locale' => 'home#index'
root :to => 'Home#index'
match '*a', :to => 'errors#routing'
end
but as I was saying, this url /admin/sign_in gives the routing error mentioned.
Dunno what I'm missing. If I remove the PasswordsController, it all works again.
PS: here is the PasswordResetsController:
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to new_user_session_path, :notice => t('password_reset_notice')
end
def edit
#user = User.find_by_reset_password_token!(params[:reset_token])
end
def update
#errors_user = nil
#user = User.find_by_reset_password_token!(params[:token])
if #user
if #user.reset_password_sent_at < 2.hours.ago
redirect_to password_reset_path, :alert => t('password_expired')
else
#user.password = params[:user][:password]
#user.password_confirmation = params[:user][:password_confirmation]
unless #user.save
#errors_user = #user.errors.full_messages.join(';<br/>') + '.'
end
unless #errors_user.nil?
#reset_token = params[:token]
render :edit
else
redirect_to new_user_session_path, :notice => t('password_change_success')
end
end
end
end
end
my login form (for users, for admin I use the standard devise one):
<div class='signin-container'>
<%= form_for(:user, :url => user_session_path(#user)) do |f| %>
<%= f.label :email, 'Email:', :id => 'username_label', :class => 'general-signin' %>
<%= f.text_field :email, :id => 'username-field', :class => 'general-signin' %>
<%= f.label :password, t('inregistrare.parola'), :id => 'pass_label', :class => 'general-signin' %>
<%= f.password_field :password, :size => 23, :id => 'pass-field', :class => 'general-signin' %>
<%= f.check_box :remember_me, :id => 'remember' %>
<%= f.label :remember_me, :id => 'remember-label' %>
<%= f.submit "", :class => 'general-signin', :id => 'buton-signin' %>
<% end %>
<a href=<%= new_user_registration_path %>><span class='creeaza'><%= t('logare.creeaza') %></span></a>
<a href=<%= password_reset_path %>><span class='forgot-pass'><%= t('forgot_password') %></span></a>
</div>
figured it out ... had to generate the devise views and edit shared/_links.erb
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
became
<%= link_to "Forgot your password?", new_password_path(:locale => I18n.locale) %><br />

Update nested attributes before saving to database

Long time reader of Stackoverflow but have never found myself in a position to ask a question (that hasn't already been answered). I guess there's a first time for everything so here it goes...
System Info:
Ruby Version = 1.8.7
Rails Version = 3.2.2
Situation:
We have an application with a user registration system in place. In order to hook up and populate all of our tables correctly, we are utilizing Complex/Nested Forms within the registration view. I actually have the nested forms working perfectly, everything is being populated as it should, its awesome really.
Here is the problem: I need to set one of the value of one of the nested attributes AFTER the form post but BEFORE the records are saved.
Here is a quick example so you can see what I'm talking about a little bit better:
A user registers with our site. When they register a record is created in the Users data table. Each user is also classified as a team_mate (join table) and assigned to their very own individual team (at first). But, a 'team' (table) also has an 'alias' field in it which, on the initial creation of the user we would like to set to the users first name (without having to have them enter their first name into an 'alias' field on the form).
So, I guess the question would be: How to I manually set the value of a nested attribute after the form post and before the records are saved to the database?
A (simplistic) example of the table schema looks is as follows:
Users (id, first_name, last_name, created_at, updated_at)
Team_mates(id, user_id, team_id, created_at, updated_at) - join table
Teams(id, alias, created_at, updated_at)
Models:
User.rb
class User < ActiveRecord::Base
has_many :team_mates, :dependent => :destroy
has_many :teams, :through => :team_mates, :foreign_key => :team_id
accepts_nested_attributes_for :team_mates, :allow_destroy => true
before_save :set_defaults
private
def set_defaults
#want to set :users => :team_mates_attributes => :team_attributes => :alias to #user.first_name here
# Would prefer to handle this here instead of in the controller.
end
end
Team.rb
class Team < ActiveRecord::Base
has_many :team_mates, :dependent => :destroy
has_many :users, :through => :team_mates, :foreign_key => :user_id
end
Team_mate.rb
class TeamMate < ActiveRecord::Base
belongs_to :user
belongs_to :team
accepts_nested_attributes_for :team, :allow_destroy => true
end
Controller
Users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
#user.emails.build(:is_default_email => 1)
#user.build_login
#user.team_mates.build.build_team(:alias => 'Clinton444', :created_at => Time.new, :updated_at => Time.new)
respond_to do |format|
format.html
format.json { render :json => #match }
end
end
def create
#user = User.new(params[:user])
#user.attributes = ({ "user" => { "team_mates" => { "team" => { "alias" => #user.first_name } } } }) #--this doesn't work...
#user.attributes = ({ :user => { :team_mates => { :team => { :alias => #user.first_name } } } }) #--neither does this...
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.json { render :json => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
end
View
new.html.haml
= form_for(#user, :html => {:class => 'form-horizontal'}) do |f|
- if #user.errors.any?
.alert
%h2
= pluralize(#user.errors.count, "error")
prohibited this post from being saved:
%ul
- #user.errors.full_messages.each do |msg|
%li
= msg
%fieldset
.control-group
= f.label :first_name, :class => "control-label"
.controls
=f.text_field :first_name, :class => "span8"
.control-group
= f.label :last_name, :class => "control-label"
.controls
=f.text_field :last_name, :class => "span8"
= f.fields_for :emails do |e|
=e.hidden_field :is_default_email, :class => "span8"
.control-group
= e.label :email, :class => "control-label"
.controls
=e.text_field :email, :class => "span8"
= f.fields_for :team_mates do |tm|
= tm.fields_for :team do |t|
=t.hidden_field :alias, :class => "span8"
=t.hidden_field :created_at, :class => "span8"
=t.hidden_field :updated_at, :class => "span8"
= f.fields_for :login do |e|
.control-group
= e.label :user_login, :class => "control-label"
.controls
=e.text_field :user_login, :class => "span8"
.control-group
= e.label :password_encrypted, :class => "control-label"
.controls
=e.text_field :password_encrypted, :class => "span8"
.control-group
.controls
=f.submit :class => 'btn btn-primary btn-medium'
And finally
Rails server output on form post
Parameters: {"user"=>{"team_mates_attributes"=>{"0"=>{"team_attributes"=>{"created_at"=>"Wed Jun 06 09:52:19 -0600 2012", "alias"=>"asfs444", "updated_at"=>"Wed Jun 06 09:52:19 -0600 2012"}}}, "first_name"=>"lkjlkjlsdfslkjeowir", "last_name"=>"ouisodifuoixv", "emails_attributes"=>{"0"=>{"is_default_email"=>"1", "email"=>"lpisfsopf#psflsjdk.com"}}, "login_attributes"=>{"user_login"=>"lkjsdfooiusfd", "password_encrypted"=>"[FILTERED]"}}, "utf8"=>"✓", "commit"=>"Create User", "authenticity_token"=>"CQLQ93/0VlncSzMlmtLPHgaVrrvjuHFN+lN6CYCsiR8="}
After looking at the models you might be wondering where emails/logins are coming from. They're built within the model on our system, but are not really part of this question so I omitted the code for them. They are working, so the problem isn't on that side.
Check http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
To support both the creation of new objects and the editing of
existing ones we have to use an array of hashes for one-to-many
associations or a single hash for one-to-one associations. If no :id
property exists then it is assumed to represent a nested model to
create.
Not 100% sure.. I haven't used\tested it before, but this should give you an idea
#user.teams.each do |team|
team.team_mates do |team_mate|
# To edit existing
team_mate.team_attributes = [ { :id => team.id, :alias => #user.first_name } ]
# To create new
team_mate.team_attributes = [ { :alias => #user.first_name } ]
team_mate.save
end
end

Calling two methods from one controller in nested model form

Through other posts on SO I've learned that my sign-up process using a nested model form is flawed in that I create a new User, then redirect to create its Profile. Here is the process:
user = User.new
user.email = ...
user.password = ...
user.profile = Profile.new
user.profile.first_name = ...
...
user.profile.save
user.save
It seems as if one solution is to initiate the profile method from within the UsersController create(?) action, so that I POST to both models(?) then redirect to a page with a form to fill out the rest of the profile.
But I'm not entirely sure how to do that, as I am new to programming/Rails. So can anyone give me guidance on how to introduce the Profile method within the UsersController? I gave it a go but don't think it's correct. Code for both Users/ProfilesController below:
User:
def new
#user = User.new
#user.profile = Profile.new
end
def index
#user = User.all
end
def create
#user = User.new(params[:user])
if #user.profile.save
redirect_to profile_new_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
Profile:
def new
#user.profile = Profile.new
end
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to profile_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
Routes.rb:
match '/signup' => 'profiles#new', :as => "signup"
get "signup" => "profiles#new", :as => "signup"
root :to => 'users#new'
resources :users
resources :profiles
My nested model form (the relevant parts):
<%= form_for(:user, :url => { :action => :create }, :html => {:id => 'homepage'}) do |f| %>
<%= f.text_field :email, :size=> 13, :id => "user[email]" %>
<%= f.fields_for :profile do |f| %>
<% end%>
<% end %>
If anyone could help me I'd greatly appreciate it.
You should have something like this in your models:
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
end
class Profile < ActiveRecord::Base
belongs_to :user
end
...of course all backed up with proper migrations. Then while building up a form you can use fields_for helper. Here is slightly modified example from docs:
<%= form_for #user do |user_form| %>
Email: <%= user_form.text_field :email %>
<%= user_form.fields_for :profile do |profile_fields| %>
First Name: <%= profile_fields.text_field :first_name %>
<% end %>
<% end %>
And update your user and his profile in the controller in one go, thanks to accepts_nested_attributes_for :profile declaration in your model.

How do I correct this model association?

I've created an album model and a photo model and added it to an existing rails application. I've made the photos model belong to the album model and the album model belong to an existing profile model that belongs to a user model. I don't know if I've associated them wrong and why I'm getting an error.
It's worth noting that when I go to URL/albums then everything works as it should but when I go to URL/profiles/1 (the code below is pasted in the show.html.erb file in the views/profile/ folder) then I get the error below.
This is a simple problem that I just can't solve. The four model files are below:
Album.rb:
class Album < ActiveRecord::Base
belongs_to :profile
has_many :photos, :dependent => :destroy
accepts_nested_attributes_for :photos, :allow_destroy => true
end
Profile.rb:
class Profile < ActiveRecord::Base
belongs_to :user
has_many :albums
def self.get_location(profile)
location = []
location << profile.city unless profile.city.blank?
location << profile.state unless profile.state.blank?
location << profile.country unless profile.country.blank?
location << profile.postal_code unless profile.postal_code.blank?
location
end
def self.missing_fields(profile)
missing = []
if profile.first_name.blank?
missing << "first name"
end
if profile.last_name.blank?
missing << "last name"
end
if profile.job_title.blank?
missing << "job title"
end
missing
end
end
Photo.rb:
require 'paperclip'
class Photo < ActiveRecord::Base
belongs_to :album
has_attached_file :upload,
:url => "/images/:id/:style/:basename.:extension",
:path => ":rails_root/public/images/:id/:style/:basename.:extension",
:styles => {
:thumb => "75x75>",
:small => "200x200>"
}
#add in any validations you may want
end
User.rb:
class User < ActiveRecord::Base
include Gravtastic
gravtastic :size => 120
# associations
has_many :albums
has_many :photos, :through => :albums
has_many :authorizations, :dependent => :destroy
has_one :profile, :dependent => :destroy
has_many :resumes, :dependent => :destroy, :order => 'created_at DESC'
has_many :thoughts, :dependent => :destroy, :order => 'created_at DESC'
has_many :user_threads, :dependent => :destroy, :order => 'created_at ASC'
accepts_nested_attributes_for :profile
# virtual attributes
attr_accessor :first_name, :last_name
# validations
validates_presence_of :first_name
validates_presence_of :last_name
validates_length_of :username, :minimum => 4, :message => " is too short"
validates :email, :email => {:message => " is not valid"}
validates_uniqueness_of :email, :case_sensitive => false
validates_uniqueness_of :username, :case_sensitive => false
validates_length_of :password, :minimum => 4, :message => " is too short"
# authlogic
acts_as_authentic do |config|
config.crypto_provider = Authlogic::CryptoProviders::MD5
config.maintain_sessions = false
config.validate_email_field = false
config.validate_login_field = false
config.validate_password_field = false
config.login_field = :email
config.validate_login_field = false
end
def self.create_from_hash!(hash)
user = User.new(:username => Time.now.to_i, :email => '', :auth_provider => hash['provider'])
user.save(:validate => false)
if hash['provider'].downcase == 'twitter'
user.profile = Profile.create(:first_name => Twitter::Client.new.user(hash['user_info'] ['nickname'].to_s).name)
else
user.profile = Profile.create(:first_name => hash['user_info']['first_name'], :last_name => hash['user_info']['last_name'])
end
user
end
def deliver_password_reset_instructions!
reset_perishable_token!
UserMailer.deliver_password_reset_instructions(self)
end
def activate!
self.active = true
save(false)
end
def deliver_activation_instructions!
reset_perishable_token!
UserMailer.deliver_activation_instructions(self)
end
end
The profile controller has this snippet:
def show
#user = User.find_by_username(params[:id])
#profile = #user.profile
#location = Profile.get_location(#profile)
#resumes = #user.resumes
#albums = #user.albums
#photos = #user.photos
#thoughts = #user.thoughts
#shouts = UserThread.find_profile_shouts(#profile)
#shouters = UserThread.find_shouters(#shouts)
#user_thread = UserThread.new
end
The view has this:
<div id="profile_right_col">
<h2>Albums</h2>
<p>
<b>Name:</b>
<%= #albums %><br />
<% #albums.photos.each do |photo| %>
<h3><%= photo.title %></h3>
<%= image_tag photos.upload.url(:small) %>
<% end %>
</p>
<%= link_to 'Edit', edit_album_path(#albums) %> |
<%= link_to 'Back', albums_path %>
</div>
The Action Controller exception shows:
ActiveRecord::StatementInvalid in Profiles#show
Showing /Users/pawel/Ruby/Apps/cvf/app/views/profiles/show.html.erb where line #137 raised:
SQLite3::SQLException: no such column: albums.user_id: SELECT "albums".* FROM "albums" WHERE ("albums".user_id = 4)
Extracted source (around line #137):
<h2>Albums</h2>
<p>
<b>Name:</b>
<%= #albums %><br />
<% #albums.photos.each do |photo| %>
<h3><%= photo.title %></h3>
You dont have #album variable defined in your show action (you have #albums and the in the views you need to go through #albums array). So its value is nil and it doesn`t have method photos.
It worked after I added Paperclip::Railtie.insert to my application.rb.

Rails: SQL Error

I'm getting a SQL error with this:
Model:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :entry
attr_accessible :body
validates :user_id, :presence => true
validates :entry_id, :presence => true
validates :body, :presence => true, :length => {:minimum => 10, :maximum => 5000} #spam/stupid protection
default_scope :order => 'comments.created at sec'
end
Controller
def show
#entry = Entry.find(params[:id])
#comments = #entry.comments.all
...
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #entry }
end
end
The view is a simple:
<% if #entry.state > 2 %>
<section id="comments">
<% #comments.each do |comment| %>
...loop some stuff...
It looks like you want :order => 'comments.created_at desc', not sec.
And what exactly is the error you are receiving?
I think your error is from here:
default_scope :order => 'comments.created at sec'
Because comments.created at sec you can't have that field in DB