Rails 4 Mailer, Amazon S3, and inline images - ruby-on-rails

I have a Rails 4 application hosted on Heroku that serves assets from an S3 bucket. I am trying to customize my mailer (in this case, a customization of the Devise mailer) so that I can embed inline images into my emails.
Per the Rails documentation, the mailer should include code such as the following:
def confirmation_instructions(record, token, opts={})
# Prepare image for embedding
attachments.inline['logo'] = File.read("#{Rails.root}/app/assets/images/logo.jpg")
# Allow Devise to do its thing
super
end
And the view should contain the following:
<%= image_tag attachments['logo'].url, :style => "my styling here" %>
On Heroku, this fails with the following log:
ActionView::Template::Error (undefined method `url' for nil:NilClass):
"my styling here" %>
In other words, it looks like attachments.inline['logo'] is returning nil, and the view is then calling .url on nil.
I've tried numerous fixes and at this point am pretty exasperated. I know it must be something simple I'm overlooking and I hope somebody out there can point out where I'm going wrong.
Thanks in advance.

Try this:
File.read(Rails.root.join("app/assets/images/logo.jpg")
That's how I got it to work.

Related

Dynamic link to download file from S3 in Rails

I have a message controller/model/view, where user can send message to each other. I have added an attachment column for user to upload files when creating their message. I would like the recipient of the message to be able to download the attached file from S3 to his/her default download folder.
I have read several post, but none of them seem to address the dynamic angle.
Below is where I am now, in my controller:
def download_url(attachment_file_name)
s3 = AWS::S3.new.buckets[Rails.application.secrets.s3_bucket_name] # This can be done elsewhere as well,
# e.g config/environments/development.rb
url_options = {
expires_in: 60.minutes,
use_ssl: true,
response_content_disposition: "attachment; filename=\"#{attachment_file_name}\""
}
s3.objects[ self.path ].url_for( :read, url_options ).to_s
end
and in the view:
<%= link_to 'Download attachment', download_url(#message.attachment) %>
But this returns the error:
ActionView::Template::Error (undefined method `download_url' for #<#<Class:0x00007fee04a12648>:0x00007fedf5c1d4d8>):
Which kind of makes sense. Does anyone knows how to do this please, i am surprise something that simple gets complex to me. help please - 4 hours in :(

Rendering an image from the Unsplash API in Rails

I have extensively researched this matter both on Stack Overflow and Google but found nothing conclusive. Since I'm completely new to the concept of API usage within Rails I have to ask for some advice.
I have followed the procedure from the github page
I have included the Unsplash helper in application_helper.rb as follows
def show_photo
Unsplash::Photo.find("tAKXap853rY")
end
and simply added
<%= image_tag show_photo %>
in my view.
This returns an object (So connectivity is good)
<img src="/images/#<Unsplash::Photo:0x007fc4b2f953c0>" alt="#
<unsplash::photo:0x007fc4b2f953c0>">
I'm aware that Rails is looking for a picture in the assets/images folder
How do I parse the inbound JSON and render it in my Rails view?
You can access to the urls key within the OpenStruct attributes in the Photo object that includes the raw, full, regular, small and thumb sizes, also as keys.
So, just to test you could use the raw one, like:
<%= image_tag Unsplash::Photo.find('tAKXap853rY')[:urls][:raw] %>
Or I think you could modify your method to accept one parameter which is the size key of the image, like:
module ApplicationHelper
def show_photo(size)
Unsplash::Photo.find("tAKXap853rY")[:urls][size.to_sym]
end
end
Then:
<%= show_photo('raw') %> # 'full', 'regular', etc ...
further to this solution I am trying to display the photographer's name by using the user.name method.
In the console I can get the following :
photo = Unsplash::Photo.find("tAKXap853rY")
photo.user.name
will return
=> "Alejandro Escamilla".
But in RAILS :
def show_photo(size)
#photo =Unsplash::Photo.find("tAKXap853rY")[:urls][size.to_sym]
end
just trying to display the name in my view like:
<%= #photo.user.name %> will return "user undefined method".
The .user.name is accessible in the console but not in rails! What have I missed? Thanks

Devise devise_error_messages! shows up without any event

I have a devise_helper.rb file where I have:
module DeviseHelper
def devise_error_messages!
'Looks like you missing a password'
end
end
But whenever I go to /user/sign_up, it shows up. I wonder why that's still showing up without an action?
Take a look at the devise helper file
devise_error_messages! is just a stub. You need to override it with your own implementation of the error messages you want or copy the views to your app and change them to your preference. It looks like you are just printing out 'Looks like you missing a password' the way you have it now. Hope this helps point you in the right direction.

Additional set of auth views with Rails/Devise

I have a Ruby on Rails app that uses Devise (and Omniauth) for authentication.
I'm trying to integrate with an iOS app (out of my control) that wants to embed pages from my app. This app needs my pages to have a specific visual appearance, so I want to create an additional set of authentication views.
After digging around in the Devise docs, I've gathered that maybe I need to create a new devise_scope block in routes.rb:
devise_scope :user do
get "iosapp/users/sign_in" => "devise/sessions#iosapp_new"
post "iosapp/users/sign_in" => "devise/sessions#iosapp_create"
delete "iosapp/users/sign_out" => "devise/sessions#iosapp_destroy"
end
And I created a new set of views that correspond to those routes:
app/views/devise/sessions/iosapp_new.html.rb
app/views/devise/sessions/iosapp_create.html.rb
app/views/devise/sessions/iosapp_destroy.html.rb
But loading /iosapp/users/sign_in in the browser leads to a Rails error:
undefined method `errors' for nil:NilClass
That error stems from line 9 of devise_helper.rb (https://github.com/plataformatec/devise/blob/master/app/helpers/devise_helper.rb):
module DeviseHelper
# A simple way to show error messages for the current devise resource. If you need
# to customize this method, you can either overwrite it in your application helpers or
# copy the views to your application.
#
# This method is intended to stay simple and it is unlikely that we are going to change
# it to add more behavior or options.
def devise_error_messages!
return "" if resource.errors.empty?
messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
...
I'm obviously doing something wrong here, but can't figure out why resource is undefined when called from my "alternate" views. It seems as if I may need to create additional controller methods as well, but I can't find anything in the docs about this.
Am I way off track? Or is there a better way to accomplish my goal than this?
Your new views need form_for resource - do you have that in place? It can't find the errors on the resource if there is no resource, hence the error on nil.

Get absolute URL for paperclip attachment

Is it possible to get the absolute URI for a Paperclip attachment? Right now, the problem is that the production environment is deployed in a sub-URI (on Passenger: RackBaseURI), but <paperclip attachment>.url returns the Rails-app relative URI (/system/images/...). Is there a way to get the absolute URI for Paperclip attachments?
I'm using Paperclip v2.7 and Rails 3.2.8.
asset_url(model.attachment_name.url(:style))
Relevant github issue
try
URI.join(request.url, #model.attachment_name.url)
or
URI(request.url) + #model.attachment_name.url
It's safe if you use S3 or absolute url.
Update: this answer is better than mine ;) https://stackoverflow.com/a/21027839/683157
According to this github issue, it is cleaner to use ActionController::Base.asset_host so it would result the helper:
def add_host_prefix(url)
URI.join(ActionController::Base.asset_host, url)
end
This supposes you have in every /config/environments/<environment>.rb file the following:
Appname::Application.configure do
# ....
config.action_controller.asset_host = 'http://localhost:3000' # Locally
# ....
end
The most widely applicable way of doing this is to first define your asset hosts in the relevant config/environment file:
config.action_controller.asset_host = "http://assethost.com"
config.action_mailer.asset_host = "http://assethost.com"
Then in views and mailers:
asset_url(model.attachment.url(:style))
In the console:
helper.asset_url(model.attachment.url(:style))
In a model:
ApplicationController.helpers.asset_url(model.attachment.url(:style))
You can do this:
<%= image_tag "#{request.protocol}#{request.host_with_port}#{#model.attachment_name.url(:attachment_style)}" %>
Or make a helper method to wrap it.
def absolute_attachment_url(attachment_name, attachment_style = :original)
"#{request.protocol}#{request.host_with_port}#{attachment_name.url(attachment_style)}"
end
And use it like this:
<%= image_tag absolute_attachment_url(attachment_name, :attachment_style)}" %>
Ex: Model = Person (#person), attachment_name = avatar, style = :thumb
<%= image_tag absolute_attachment_url(#person.avatar, :thumb)}" %>
This doesn't solve the original poster's problem exactly (it operates in the view, not the model), but may be helpful for people who are trying to "get absolute URL for paperclip attachment" within their view: In the same way that
image_tag(user.avatar.url(:large))
puts the image itself into your view,
image_url(user.avatar.url(:large))
returns just the URL you'll need if you want to link to the asset directly (e.g. in a link_to call).
You can add to your application.rb (or for specific enviroment in config/environments/*):
config.paperclip_defaults = {
url: "http://my.address.com/system/:class/:attachment/:id_partition/:style.:extension",
path: ':rails_root/public/system/:class/:attachment/:id_partition/:style.:extension',
}
Restart and reimport your images.
PS: obviously you can replace http://my.address.com with an environment variable.

Resources