So I'm working with carrier wave direct, and I'm at the point where I need to update the "key" attribute on the uploader object. Here's the relevant line on the docs:
After uploading to S3, You'll need to update the uploader object with the returned key in the controller action that corresponds to new_user_url:
#uploader.update_attribute :key, params[:key]
The problem is that my #uploader object doesn't have an update_attribute method. In fact, when I look at all of the methods on the #uploader object, I see methods like key() and key=(), but no update_attribute.
Any idea what's going on? Did I miss some setup step that I need to perform to make the update_attribute method available?
I got it to work by calling update_attribute on the model and not the uploader. In the below snipped the #uploader the subclass of CarrierWave::Uploader::Base and #video is the model.
def upload
#uploader = Video.new.asset
#uploader.success_action_redirect = videos_upload_successful_url
end
def upload_successful
#video = Video.new
#video.update_attribute :key, params[:key] # different than documentation!!
#video.save
end
This seems to be contrary to the documentation where it is documented they way you tried it.
I had an same issue, but I solved the problem to add new column.
I think you need to add another column like avatar_image_url aside from avatar which is used as mount_uploader :avatar, AvatarUploader.
And the controller is something like this:
class ProfilesController < ApplicationController
before_action :setup_context
def edit
#uploader = #profile.avatar
#uploader.success_action_status = '201'
end
def update
if #profile.update_attributes(profile_params)
redirect_to home_path
else
render :edit
end
end
private
def setup_context
#profile = current_user
end
def profile_params
params.require(:user).permit(:name, :avatar_image_url)
end
end
When you post the following parameters, you can save avatar_image_url.
Parameters: {"utf8"=>"✓", "user"=>{"avatar_image_url"=>"https://my-development.s3.amazonaws.com/uploads/220f5378-1e0f-4823-9527-3d1170089a49/foo.gif", "name"=>"Schneems"}}
You can refer the image like this.
<%= image_tag #user.avatar_image_url %>
And also you can check this out.
Direct to S3 Image Uploads in Rails | Heroku Dev Center
Related
I'm trying to create and order that is associated with an item.
An Order has one item:
class Order < ActiveRecord::Base
has_one :item
end
An Item belongs to an order:
class Item < ActiveRecord::Base
belongs_to :user
end
According to the guide this should work:
build_association(attributes = {})
create_association(attributes = {})
I have this in my controller:
def create
#order = #current_item.build_order(order_params)
#order.save
redirect_to #order
end
And this is the error I'm getting:
undefined method `build_order' for nil:NilClass
I know this has to do with how I've defined current_items but I've tried many different things and all lead to this same error message.
I have this in my application helper:
def current_item
Item.find(params[:id])
end
Can anyone point me in a better direction for how to define this or what I'm doing wrong here. Thanks for your help!
1) You don't have access to a helper method from the controller. You can include the helper class in your controller but it's a really bad practice. You must use helper methods only in the views.
2) You can move current_item method from the helper to the controller. Then there will be another problem. In your create method, you are trying to access instance variable #current_item which is not initialized at the moment, not the method. You can do it this way:
#order = #current_item.build_order(order_params)
to
#order = current_item.build_order(order_params)
Then current_item will return you Item object.
3) I am not sure what are your params, but you can implement it this way:
def create
#order = Order.new(params[:order])
#order.save
redirect_to #order
end
where params[:order] is for example:
{name: "order 1", item_id: 1}
You should change your create to use a method, rather a variable, so modify it as follows:
def create
#order = current_item.build_order(order_params)
#order.save
redirect_to #order
end
# rest of code
def current_item
Item.find(params[:id])
end
This should help.
Good luck!
The error you're getting is being caused by trying to run Item.find(params[:id]) but not passing it a valid value. It seems that params[:id] is maybe nil? Can you confirm this using a debugger or by temporarily adding raise "Params[:id] is set to #{params[:id]} to the first line of the method, running the code and seeing what it says in the terminal output?
All you need to do make this work is have a parameter value for the item come from the form that is being submitted. Normally rails uses the route/url to populate the value of params[:id]. For example, when the request is GET /items/1, params[:id] is 1.
In this case though, unless you've done some custom routing that you haven't shown in your question, creating a new order would usually be a POST to /orders and since there is no id in the url, params[:id] is nil.
It's up to you to add the item id from the order form. It would make sense that it would be sent with the rest of the order params as item_id, rather than just id, since id is usually used to reference the current object, which is a new order and therefore doesn't get have an id.
You'll need to make sure that item_id is whitelisted in your strong params with the rest of the values in the order_params method (I assume you defined this in the same controller but did not show it in the code), and then the code would look something like this.
def create
#order = current_item.build_order(order_params)
#order.save
redirect_to #order
end
#note the changes the the argument
def current_item
Item.find(order_params[:item_id])
end
def order_params
params.require(:order).permit(:item_id, :other_values_that_you_send)
end
This is my code:
class ApplicationsController < ApplicationController
def new
#application = Application.new
end
def create
#application = Application.new(application_params)
#layout = Layout.find_or_create_by(application_id: #application.id)
if #application.save
redirect_to #application
else
render 'new'
end
end
layout belongs_to :application
When I check the Layouts table it is empty. Can you help me, please?
Your model contains the following validations:
validates :adv_path, presence: true
validates :start_time, presence: true
validates :end_time, presence: true
Therefore you are not able to create a Layout without this values. You must do something like this (with useful values):
Layout.find_or_create_by(id: #application.id) do |layout|
layout.adv_path = 'A useful default'
layout.start_time = 1.second.ago
layout.end_time = 100.year.from_now
end
Or rethink the need for the validators.
In your layout creation line, #application doesn't have an id yet. Resultantly, you pass 'nil' to the #layout's application_id which makes its validation fail. (You mentioned the layout's application presence validation in a comment).
So create the layout after #application is saved and you should be good to go.
if #application.save
#layout = Layout.create(application_id: #application.id)
When you use new method like
#application = Application.new(application_params)
it does not persist that record to db, other words it does not have id. you should use method create instead
#application = Application.create(application_params)
then #application will be persisted to db, and when you say find_or_create_by it will find with id, and not search for id nil
def create
#application = Application.new(application_params)
#layout = Layout.find_or_create_by(application_id: #application.id) # <== that line
if ...
....
end
end
That line is rather misleading. #application.id is nil. So the first time, you'll create a layout record with a application_id as nil. The next time, it'll find the record with application_id: nil and use that. So it'll just create one single record and forever use it.
If you are creating a layout every time you create an application, consider doing it this way:
def create
#application = Application.new(application_params)
if #application.save
#layout = #application.layouts.create( ... ) # assuming application has_many :layouts
redirect_to #application
else
render 'new'
end
end
Edit: it turns out I made a very simple mistake and had a Template that was associated with a LocalTemplate id that no longer existed. If anyone has this problem and thinks that they somehow are unable to unable to associate the id of another model in their update action, make sure that you didn't accidentally delete the parent object causing that id to no longer exist!
The code below, while dramatically simplified did work for me.
I have a Template model in my rails app. It has a method "data" defined in it.
I am able to access this method in the create and show actions with #template.data, however when using the same #template.data in the update action of my controller I get a no method error because I am not showing the correct local template id to it. This line can be found in the model where it reads base_data = YAML.load(local_template.data)
I stored an id of the associated local_template when initially saving a new template, but how can I make sure I reference that id again in the update action so I do not get a no method error?
Here is a simplified version of the Template model and controller
Model:
class Template < ActiveRecord::Base
def data
base_data = YAML.load(local_template.data)
# couldn't pass the correct LocalTemplate here because
# the local_template_id I had in my Template model no
# longer existed. Changing the id to a LocalTemplate
# that did exist fixed the issue.
end
end
Controller:
class TemplatesController < ApplicationController
def index
#business = Business.find(params[:business_id])
#templates = #business.templates.all
end
def new
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.build
end
def create
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.build(template_params)
if #template.save
#template.data #works fine here
redirect_to business_url(#template.business_id)
else
render 'new'
end
end
def show
#business = Business.find(params[:business_id])
#template = #business.templates.find(params[:id])
#template.data #works fine here too
end
def edit
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.find(params[:id])
end
def update
#business = Business.find(params[:business_id])
#template = #business.templates.find(params[:id])
if #template.update_attributes!(pass_template_params)
Api.new.update_template(#template.data.to_json) #this is where I had a problem
redirect_to business_url(#template.business_id)
else
render 'edit'
end
end
end
You are mixing a lot. There is a lot to refactor in your controller...
First of all, your TemplatesController should be about the template resources, but your controller looks more like a BusinessesController. In general your update action for example should look more like:
def update
#template = Template.find params[:id]
#template.attributes = template_params # though this should raise a NoMethodError, because you dind't define it; I'd prefer params[:template] if possible
if #template.save
redirect_to business_url(#template.business_id)
else
#local_templates = LocalTemplate.all
render 'edit'
end
end
Instantiating #business and #local_templates makes non sense, because you don't use it at all. Speed up your responses if you can! :)
Fixed that, there is no need for the overhead of a nested resource in update (as you did).
If saving #template fails for validation reasons, you better should load the business object late by:
#template.business
in your /templates/edit.html.erb partial. Then you also do not need a nested route to your edit action... You see, it cleans up a lot.
As a general guideline you should create as less as possible controller instance variables.
If you cleaned up your controller and views, debugging your data issue will be easier.
I assume:
local_template
in your Template model to be an associated LocalTemplate model object. So it should no issue to call that anywhere if you ensured the referenced object exists:
class Template < ActiveRecord::Base
def data
return if local_template.nil?
YAML.load(local_template.data)
end
end
or validate the existence of the local_template object. or even b
You should confirm #template is not nil, if #template is nil, you can't use data method.
1.9.3-p547 :024 > nil.data
NoMethodError: undefined method `data' for nil:NilClass
from (irb):24
from /Users/tap4fun/.rvm/rubies/ruby-1.9.3-p547/bin/irb:12:in `<main>'
And you should use update_attributes!, it can raise an exception if record is invalid.
You can do like this.
if #template
#template.update_attributes!(template_params)
#template.data
end
In my Rails app, I enable users to upload images using Carrierwave and Amazon S3. I want to implement a feature that lets users edit existing images by rotating it 90 degrees.
I'm confused about where this code would go. Does it go in the image uploader file, or the image controller? And how is it called? I believe it should look something like this:
image = Image.find(params[:id])
image_obj = MiniMagick::Image.read(image.file)
image_obj.rotate(-90)
image_obj.write(image.file)
But I haven't been able to find examples to help me. If anyone can give me a pointer in the right direction, I would really appreciate it!
Edit
Thanks to deep for their thorough response! Here is what I ended up doing:
In my view:
# image.html.erb:
<%= link_to rotate_image_path(:id => image.id), :remote => true %>
In my controller:
# image_controller.rb:
def rotate
#image = Image.find(params[:id])
#image.rotated = true
#image.save
respond_to do |format|
format.js { render :nothing => true }
end
end
In my model:
# image.rb
attr_accessible :rotated
after_save :rotate_image, if: ->(obj){obj.rotated.present? && obj.rotated?}
def rotate_image
self.image_path.recreate_versions!
end
In my uploader:
# image_uploader.rb
process :rotate_img
def rotate_img
if model.rotated.present? && model.rotated?
manipulate! do |img|
img.rotate '-90'
img
end
end
end
The only real change I made was in the uploader, where I ran into errors trying to do a condition process. I put the conditional within the rotate_img method.
Here's my solution
First define a attribute accessor in your model and on update set it to true.
In your model
#image.rb
attr_accessor :rotate
In your controller
#images_controller.rb
def update
#image = Image.find(params[:id])
#image.rotate = true
#image.save
redirect_to root_path, :notice => "Bla bla bla"
end
Carrierwave provides a recreate_versions! method which will process and re-upload the image. In you case you can add a after_save callback that will trigger recreate_versions! method only if the rotate attribute is set to true.
In your model
#image.rb
after_save :rotate_image, if: ->(obj){ obj.rotate.present? and obj.rotate? }
def rotate_image
self.file.recreate_versions!
end
Now in your image uploader you can write the code to rotate a image.
#image_uploader.rb
.......
# It will replace the original image with rotated version
process :rotate_img, :if => model.rotate.present and model.rotate?
def rotate_img
manipulate! do |img|
img.rotate "90"
img #returns the manipulated image
end
end
If you don't want to replace the original image then all you have to do is to call process inside a version like
# Create different versions of your uploaded files:
version :rotated_img do
process :rotate_img
end
I would like to remove this logic:
Suitcase::Hotel.find(id: hotel.id).images.first.url
from view.
https://gist.github.com/2719479
I dont have model Hotel. I get this url via API using Suitcase gem.
Problem is because
hotel is from #hotels = Suitcase::Hotel.find(location: "%#{headed}%") and API recevie me images only if do Suitcase::Hotel.find(id: hotel.id)
If Suitcase::Hotel.find(id: hotel.id).images.first.url works then i would guess hotel.images.first.url will work too if hotel is an hotel instance.
Is adding:
#hotel = Suitcase::Hotel.find(id: hotel.id)
to #show action doesn't work?
EDIT:
In that case make an helper:
def hotel_image_url(hotel)
Suitcase::Hotel.find(id: hotel.id).images.first.url
end
But as I can see here you can simply write in controller:
#hotels_data = Suitcase::Hotel.find(ids: #hotels.map(&:id))
Or to be more elegant add to your model (or create decorator (it's better option)):
def photo
Suitcase::Hotel.find(id: self.id).images.first.url
end
I think this should work, not sure about the second option
class Search < ActiveRecord::Base
attr_accessible :headed, :children, :localization, :arriving_date, :leaving_date, :rooms, :adults
def hotels
#hotels ||= find_hotels
end
private
def find_hotels
return unless headed.present?
#hotels = Suitcase::Hotel.find(location: "%#{headed}%")
#hotels.each do |hotel|
def hotel.image_url
Suitcase::Hotel.find(id: hotel.id).images.first.url
end
end
end
end
# or this, but I'm not sure if this works
#hotels.each do |hotel|
image_url = Suitcase::Hotel.find(id: hotel.id).images.first.url
def hotel.image_url
image_url
end
end