Rails 3.0 associated model with download 'save to' image - ruby-on-rails-3

My Rails 3.0.3 application has a scaffold 'month' which has a link where the user can download an image using 'save to'.
Now I need to make an association where the month model belongs_to the wallpaper model.
Routes:
root :to => 'inicio#index'
resources :wallpapers do
resources :months
end
# the route that works with no association
# match 'download/:id' => 'months#download', :as => :download
# the route I tried
match 'wallpapers/:id/months/:id' => 'months#download', :as => :download
Month model:
class Month < ActiveRecord::Base
belongs_to :wallpaper
has_attached_file :wallpaper_picture, :styles => {
:default => { :geometry => '530x330', :quality => 80, :format => 'jpg'}
}
end
Wallpaper model with friendlyid:
class Wallpaper < ActiveRecord::Base
has_many :months, :dependent => :destroy
extend FriendlyId
friendly_id :title, :use => :slugged
end
In months_controller I made the download method, this method works with no association:
class MonthsController < InheritedResources::Base
belongs_to :wallpaper, :finder => :find_by_slug!
def download
#wallpaper = Wallpaper.find(params[:wallpaper_id])
#month = #wallpaper.month.find(params[:id])
send_file #month.wallpaper_picture.path,
:filename => #month.wallpaper_picture_file_name,
:type => #month.wallpaper_picture_content_type,
:disposition => 'attachment'
end
end
View months/index
- #months.each do |month|
= link_to image_tag(month.wallpaper_picture(:default)), wallpaper_month_path(month.wallpaper, month)
I've tried changing in the months_controller the download method, but it is wrong:
#months = Wallpaper.month.conditions({:person_id => some_id})

Here is how I got it
Routes
resources :wallpapers do
resources :months
end
match 'wallpaper/:wallpaper_id/download/:id' => 'months#download', :as => :download
In the routes I must pass the :wallpaper_id (has_many :months),
the :id is the id of the current controller (belongs_to :wallpaper)
'download' will be the name of the path used in the view 'download_path'
In this path I must pass the foreign key and the id
View months/index
- #months.each do |month|
= link_to 'Download Picture', download_path(month.wallpaper_id, month.id)
In months_controller the download method will receive those parameters and pass the associated image to the send_file method.
def download
#wallpaper = Wallpaper.find(params[:wallpaper_id])
#month = #wallpaper.months.find(params[:id])
send_file #month.wallpaper_picture.path,
:filename => #month.wallpaper_picture_file_name,
:type => #month.wallpaper_picture_content_type,
:disposition => 'attachment'
end
PD: if send_file fails in Production, change it to send_data or
comment out this line in config/production.rb
config.action_dispatch.x_sendfile_header = "X-Sendfile"
send_file just sends an empty file

Related

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

ruby on rails app tutorial can't display micropost page

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

Multiple (n) identical nested forms generated square-times(n*n) when validation fails

User has two addresses shipping(:address_type=0) and billing(:address_type=1)
User form with 2 classic nested forms for each address type are generated square times every submit and failed validation.
Models:
class User < ActiveRecord::Base
has_many :addresses, :dependent => :destroy
accepts_nested_attributes_for :addresses
validates_associated :addresses
end
class Address < ActiveRecord::Base
belongs_to :user
validates :user, :address_type, :first_name, :last_name, :street
end
Controller
class UsersController < ApplicationController
public
def new
#user = User.new
#shipping_address = #user.addresses.build({:address_type => 0})
#billing_address = #user.addresses.build({:address_type => 1})
end
def create
#user = User.new(params[:user])
if #user.save
#fine
else
render => :new
end
end
Uncomplete Form
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for :addresses, #shipping_address do |f|
=f.hidden_field :address_type, :value => 0
=ff.fields_for :addresses, #billing_address do |f|
=f.hidden_field :address_type, :value => 1
=ff.submit
The form should look like this:
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for :addresses do |f|
Nothing else.
Addressess is already a collection, so you should have just one rendering of it.
Also that ":addresses, #shipping_address" makes it to render addresses AND shipping address, even if it's included in #user.addresses.
The addressess built in new action will show there because they are in the addresses collection.
EDIT:
If you need only these two addresses, you can sort it and pass it to fields_for directly:
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for ff.object.addresses.sort{|a,b| a.address_type <=> b.address_type } do |f|
That should do it.
Surprised? I guess not but I was. I found it am I correct? And its stupid and simple.
There is no #shipping_address nor #billing_address when validation fails and rendering the new action (the form) again. But #user has already 2 addresses builded and nested form behave correctly to render each twice for first time failed validation.
def create
#user = User.new(params[:user])
if #user.save
#fine
else
#user.addresses.clear
#user_address = #user.addresses.build({:address_type => 0})
#user_address.attributes = params[:user][:addresses_attributes]["0"]
#billing_address = #user.addresses.build({:address_type => 1})
#billing_address.attributes = params[:user][:addresses_attributes]["1"]
render => :new
end
end

paperclip custom path and url for polymorphic associations

I am using attachment model as polymorphic association. How to change path and url parameters based on the associations.
Attachment Model
class Attachment < ActiveRecord::Base
belongs_to :user
belongs_to :attachable, :polymorphic => true
has_attached_file :attachment,
:url => "/attachments/:id/:basename.:extension",
:path => ":rails_root/public/attachments/:id/:basename.:extension",
:default_url => "/attachments/original/no-file.txt"
end
Project Model
class Project < ActiveRecord::Base
...
has_many :attachments, :as => :attachable, :dependent => :destroy
end
Client
class Client < ActiveRecord::Base
...
has_many :attachments, :as => :attachable, :dependent => :destroy
end
following path files are saving fine.
:path => ":rails_root/public/attachments/:id/:basename.:extension",
but I need to save files based on association like, for this how do pass the parameters to 'path'. 'attachable_type' defines the which association upload file belongs
/attachments/project/
/attachments/client/
You can use Paperclip Interpolations. Interpolations allows you to call a method to determine the value of a part of the path.
class Attachment < ActiveRecord::Base
belongs_to :user
belongs_to :attachable, :polymorphic => true
Paperclip.interpolates :attached_to do |attachment, style|
attachment.instance.attachable.class.to_s.downcase
end
has_attached_file :attachment,
:url => "/attachments/:id/:basename.:extension",
:path => ":rails_root/public/attachments/:attached_to/:id/:basename.:extension",
:default_url => "/attachments/original/no-file.txt"
end
has_attached_file :attachment, :path => ":rails_root/public/attachments/#{lambda { |a| a.instance.images_path? ? 'project' : 'client' }}/:id/:basename.:extension"
def images_path?
if your pretty condition
#return true
else
#return false
end
end

Configuring Rails 3 + Polymorphic Image model + Paperclip and Amazon S3, No errors, but nothing uploading

I suspect the problem lies in the way I am creating the polymorphic image attribute. I am using fields_for in the form.
In this case a user can create a post and add an image using paperclip, storing it with S3.
I am using a polymorphic image model "post_image":
class PostImage < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
#.merge(PAPERCLIP_OPS)
has_attached_file :image, :styles => { :medium => "200x200>", :thumb => "50x50>" },
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => "/:style/:id/:filename",
:bucket => "zounds-dev"
validates_attachment_presence :image
validates_attachment_size :image, :less_than => 5.megabytes
end
Post Model:
class Post < ActiveRecord::Base
has_many :post_images, :as => :imageable, :dependent => :destroy
.
.
.
accepts_nested_attributes_for :post_images, :reject_if => lambda { |t| t[:post_image].nil?}, :allow_destroy => true
end
New Post Form:
=form_for( setup_post(#post,current_user), :html => { :multipart => true}) do |f|
%dl
=f.fields_for :post_images do |ff|
=ff.file_field :image
%dt.field=f.label :name
%dd.field=f.text_field :name
%dt.field=f.label :description
%dd.field=f.text_area :description
=f.fields_for :user do |u|
=render "user_fields", :f => u
=f.fields_for :assignments do |ff|
=ff.check_box :_destroy, {:checked => ff.object.persisted?}, '0','1'
=ff.label :_destroy, ff.object.group.name
=ff.hidden_field :group_id
.action=f.submit "Save Post"
The setup_post helper method used in the Post form_for: (the groups stuff isn't relevant here)
def setup_post(post, current_user)
groups = current_user.groups_as_owner + current_user.groups_as_member
(groups - post.groups).each do |group|
post.assignments.build(:group => group)
end
post.assignments.sort_by {|x| x.group.name }
post_image = post.post_images.build
post
end
Post controller:
def new
#user = User.find(params[:user_id])
# #post = #user.posts.build
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #post }
end
end
def create
#user = current_user
#post = #user.posts.build(params[:post])
.
.
.
end
I suspect the problem is that I am using fields_for for the post_image attribute, but I've looked all over and can't figure out what the proper way to implement a polymorphic nested image attribute is.
I also did the s3sh amazon s3 console thing, and although I couldn't upload an image because I couldn't figure out how to pass in the right image path to the open() function, I connected to S3. My s3.yml file is set up correctly as well.
Thanks yall,
Brian
The issue was with the reject_if in accepts_nested_attributes for the Post model
accepts_nested_attributes_for :post_images, :reject_if => lambda { |t| t[:post_image].nil?}, :allow_destroy => true
commenting it out fixed the issue.