rails - Render template and zip - ruby-on-rails

I'm trying to build a KML file in Rails, which I have done successfully, but now I want to provide a KMZ format as well which would render the index.kml file and zip it. Here is where I get stumped. I have updated the MIME Types as follows.
Mime::Type.register_alias "application/vnd.google-earth.kml+xml", :kml
Mime::Type.register_alias "application/vnd.google-earth.kmz", :kmz
Here is my format block
def index
#map_items = Items.all
respond_with(#map_items) do |format|
format.kml
format.kmz { NOT SURE WHAT IS BEST TO DO }
format.georss
end
end
ANy help would be much appreciated. Thanks!

I figured out a way to do this with Delayed Job. Every time the points are updated or created I fire off the MapOverlayJob.
class MapsController < ApplicationController
def overlay
#points = Points.all
return render_to_string("overlay.kml")
end
end
class MapOverlayJob
def initialize
#s3_filename ||= "maps/overlay.kmz"
#zip_filename ||= "overlay.kml"
end
def perform
AWS::S3::S3Object.store(#s3_filename,
build_kmz_file,
S3_BUCKET,
:access => S3_ACL,
:content_type => Mime::KMZ)
end
private
def build_kmz_file
Zippy.new(#zip_filename => MapsController.new.overlay).data
end
end

Related

Undefined method for array csv export

I'm getting the following error when trying to export to a csv:
undefined method `dd_export' for #<Array:0x00007fc4836f1798>
I think it's the relationship between the model and controller, but can't work out why this is. I'm using a custom dd_export convention as more csv download may be added, and also using to_csv doesn't seem to export what's required in my model.
day_degree_export.rb
class DayDegreeExport < ApplicationRecord
def self.dd_export
attributes = %w{Date Min_Temp Max_Temp}
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |dd|
csv << [
dd['date'],
dd['variables'][1]['value']
dd['variables'][0]['value']
]
end
end
end
end
end
day_degree_export_controller.rb
class DayDegreeExportController < ApplicationController
dd_results
data = HTTParty.get("url_link")
#ddexport = JSON.parse(data.body)
respond_to do |format|
format.html
format.csv { send_data #ddexport['data'].dd_export, filename: "ddexport.csv" }
end
end
end
I have required csv in my application.rb.
If you want to call #ddexport['data'].dd_export, you'd have to patch the Array class for dd_export, but a better way to implement it is to save your array as DayDegreeExport records.
class DayDegreeExportController < ApplicationController
def dd_results
data = HTTParty.get("url_link")
#ddexport = JSON.parse(data.body)
DayDegreeExport.create(#ddexport) # you can do this if your #ddexport is array of hashes with matching keys
respond_to do |format|
format.html
format.csv { send_data DayDegreeExport.dd_export, filename: "ddexport.csv" }
end
end
end
Since you are not intending to store #ddexport['data'], then there is no need for DayDegreeExport to inherit from ApplicationRecord. Instead, I would suggest you do something like:
# app/services/day_degree_export_service.rb
class DayDegreeExportService
CSV_ATTRIBUTES = %w(
Date
Min_Temp
Max_Temp
).freeze
attr_accessor *%w(
args
).freeze
class << self
def call(args={})
new(args).call
end
end
def initialize(args)
#args = args
end
def call
CSV.generate(headers: true) do |csv|
csv << CSV_ATTRIBUTES
dd_export_data.each do |dd|
csv << [
dd['date'],
dd['variables'][1]['value']
dd['variables'][0]['value']
]
end
end
end
private
def dd_export_data
#dd_export_data ||= JSON.parse(HTTParty.get("url_link"))['data']
end
end
Which you would use something like:
class DayDegreeExportController < ApplicationController
def dd_results
respond_to do |format|
format.html
format.csv { send_data DayDegreeExportService.call, filename: "ddexport.csv" }
end
end
end
Now, a few comments:
1) I never use CSV so I have no idea whether the code is going to work. I took a look at the docs and I think this may be close, but you may have to fiddle with it.
2) A PORO (plain old ruby object) gets the job done here. You can see I suggest you place it in a services directory and rails will pick it up automatically.
3) In the class-level call method, I include optional args. That's just for example purposes. Some other time you might want to pass in arguments.
4) For services, I like to use the call method instead of service-specific methods like dd_results. It's just a me thing. But, then, you don't have to think about what to call your methods in your service. As long as your services and their purpose have a 1:1 relationship, then the class name describes what the service does instead of the method name.
5) Using class << self creates class methods. It's basically the same as doing def self.call.
6) This all assumes, I supposed, that JSON.parse(HTTParty.get("url_link"))['data'] returns an array of data and that each datum has the keys that you indicate.

How to redirect URLs without format / extension?

In my Rails 4.2 app I have a simple controller action that downloads a ZIP file:
class PresskitController < ApplicationController
def download
respond_to do |format|
format.zip do
send_data(Presskit.new, :filename => "presskit.zip")
end
end
end
end
This URL works great: http://www.myapp.com/presskit.zip
Is there a way to make this URL work without the extension as well? So that clicking on http://www.myapp.com/presskit would also trigger the download?
Thanks for any help.
Just use the send_data for your action? as you don't care about the format
class PresskitController < ApplicationController
def download
send_data(Presskit.new, :filename => "presskit.zip")
end
end

Domain name missing in image paperclip in to_json rails 4

I have the model that uses paperclip like this
has_attached_file :image, styles: { :medium => "50x50>" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
def image_url
image.url(:medium)
end
I need it Json, So in my controller,
respond_to do |format|
format.json { render json: #celebrity.to_json(:methods => [:image_url])}
end
And the result is
"image_url":"/system/celebrities/images/000/000/003/medium/Designed___developed_by_wd_ApS.png?1430926991"
but, I need to include the domain name, localhost:3000 ,
So what I have to do here
Try this.
Create module:
module WithDomain
def domain
#domain
end
def domain=(val)
#domain = val
end
def domain?
#domain.present?
end
end
Change you model accordingly:
class Celebtiry < ActiveRecord::Base
include WithDomain
# existing staff
def image_url
if domain?
URI.join(domain, image.url(:medium)).to_s
else
image.url(:medium)
end
end
end
and in your controller:
respond_to do |format|
format.json {
#celebrity.domain = request.base_url
render json: #celebrity.to_json(:methods => [:image_url])
}
end
Solution 1: (with existing code)
You can use asset_url from ActionView::Helpers::AssetUrlHelper module which will give you the absolute url of your image. Just include ActionView::Helpers::AssetUrlHelper this in your model so that asset_url becomes available inside your model.
So, your method inside the model would be:
include ActionView::Helpers::AssetUrlHelper
def image_url
asset_url(image.url(:medium))
end
This is the easiest solution for you with your current code.
Solution 2: (inside the controller)
In your controller request is available, so you can do:
URI.join(request.url, #celebrity.image.url(:medium))
which will give you the absolute url of the image. This will be an URI object, which can be converted to a String with .to_s method.
Here is the issue from paperclip from where this solution is derived. Hope this helps.

Rails - Limited Browsing of User Attachments using ckeditor Gem w/ CanCan & Paperclip

I have everything working correctly, but now I want to limit some user Abilities to perform some attachment actions.
Specifically, the ability to limit the viewing of all uploaded attachments, to those actually uploaded by the User.
Here is the applicable snippet from ability.rb I tried ...
if user.id
can :access, :ckeditor
can [:read, :create, :destroy], Ckeditor::Picture, assetable_id: user.id
can [:read, :create, :destroy], Ckeditor::AttachmentFile, assetable_id: user.id
end
The situation arises when I am using the CKeditor UI, click the Image button, and then click the Browse Server button to see the previously uploaded images -- right now the image browser shows the uploads of all users. I would like the viewed images to be limited to those of the current_user only.
Since the Ckeditor table saves the assetable_id of the attachment (i.e. the user.id), and the logic above does not work on its own, I'm guessing some custom Controller logic is also needed here.
Thanks.
I was able to solve this issue with custom Ckeditor controllers & some guidance from here:
https://github.com/galetahub/ckeditor/issues/246
First I needed to make copies of the Ckeditor controllers pictures_controller.rb & attachment_files_controller.rb and place them here:
/app/controllers/ckeditor/
Then a few updates to their suggestions to update index were necessary, particularly picture_model.find_all needed to be picture_adapter.find_all in pictures_controller.rb (and similarly attachment_file_adapter.find_all in attachment_files_controller.rb)
The key to it all is setting the proper scope with: ckeditor_pictures_scope(assetable_id: ckeditor_current_user) & ckeditor_attachment_files_scope(assetable_id: ckeditor_current_user)
Once these revisions are in place, the file browsers for pictures & attachments show only the appropriate files for that user.
Here are the revised files ... the changes are on line 4 of both.
/app/controllers/ckeditor/pictures_controller.rb
class Ckeditor::PicturesController < Ckeditor::ApplicationController
def index
#pictures = Ckeditor.picture_adapter.find_all(ckeditor_pictures_scope(assetable_id: ckeditor_current_user))
#pictures = Ckeditor::Paginatable.new(#pictures).page(params[:page])
respond_with(#pictures, :layout => #pictures.first_page?)
end
def create
#picture = Ckeditor.picture_model.new
respond_with_asset(#picture)
end
def destroy
#picture.destroy
respond_with(#picture, :location => pictures_path)
end
protected
def find_asset
#picture = Ckeditor.picture_adapter.get!(params[:id])
end
def authorize_resource
model = (#picture || Ckeditor.picture_model)
#authorization_adapter.try(:authorize, params[:action], model)
end
end
/app/controllers/ckeditor/attachment_files_controller.rb
class Ckeditor::AttachmentFilesController < Ckeditor::ApplicationController
def index
#attachments = Ckeditor.attachment_file_adapter.find_all(ckeditor_attachment_files_scope(assetable_id: ckeditor_current_user))
#attachments = Ckeditor::Paginatable.new(#attachments).page(params[:page])
respond_with(#attachments, :layout => #attachments.first_page?)
end
def create
#attachment = Ckeditor.attachment_file_model.new
respond_with_asset(#attachment)
end
def destroy
#attachment.destroy
respond_with(#attachment, :location => attachment_files_path)
end
protected
def find_asset
#attachment = Ckeditor.attachment_file_adapter.get!(params[:id])
end
def authorize_resource
model = (#attachment || Ckeditor.attachment_file_model)
#authorization_adapter.try(:authorize, params[:action], model)
end
end

Trouble rendering a view inside a generic class

I'm trying to encapsulate the logic for generating my sitemap in a separate class so I can use Delayed::Job to generate it out of band:
class ViewCacher
include ActionController::UrlWriter
def initialize
#av = ActionView::Base.new(Rails::Configuration.new.view_path)
#av.class_eval do
include ApplicationHelper
end
end
def cache_sitemap
songs = Song.all
sitemap = #av.render 'sitemap/sitemap', :songs => songs
Rails.cache.write('sitemap', sitemap)
end
end
But whenever I try ViewCacher.new.cache_sitemap I get this error:
ActionView::TemplateError:
ActionView::TemplateError (You have a nil object when you didn't expect it!
The error occurred while evaluating nil.url_for) on line #5 of app/views/sitemap/_sitemap.builder:
I assume this means that ActionController::UrlWriter is not included in the right place, but I really don't know
Does this do what you're trying to do? This is untested, just an idea.
in lib/view_cacher.rb
module ViewCacher
def self.included(base)
base.class_eval do
#you probably don't even need to include this
include ActionController::UrlWriter
attr_accessor :sitemap
def initialize
#av = ActionView::Base.new(Rails::Configuration.new.view_path)
#av.class_eval do
include ApplicationHelper
end
cache_sitemap
super
end
def cache_sitemap
songs = Song.all
sitemap = #av.render 'sitemap/sitemap', :songs => songs
Rails.cache.write('sitemap', sitemap)
end
end
end
end
then wherever you want to render (I think your probably in your SitemapController):
in app/controllers/sitemap_controller.rb
class SitemapController < ApplicationController
include ViewCacher
# action to render the cached view
def index
#sitemap is a string containing the rendered text from the partial located on
#the disk at Rails::Configuration.new.view_path
# you really wouldn't want to do this, I'm just demonstrating that the cached
# render and the uncached render will be the same format, but the data could be
# different depending on when the last update to the the Songs table happened
if params[:cached]
#songs = Song.all
# cached render
render :text => sitemap
else
# uncached render
render 'sitemap/sitemap', :songs => #songs
end
end
end

Resources