I'm trying to use the Rails Atom Feed Helper to generate a feed for a nested resource. My view template (index.atom.builder) is:
atom_feed(:schema_date => #favourites.first.created_at) do |feed|
feed.title("Favourites for #{#user.login}")
feed.updated(#favourites.first.created_at)
#favourites.each do |favourite|
feed.entry(favourite, :url => favourite.asset.external_ref) do |entry|
entry.title(favourite.asset.external_ref)
entry.content(image_tag(favourite.asset.location), :type => 'html')
entry.author do |author|
author.name(#user.login)
end
end
end
end
And I have the following routes:
map.namespace :public do |pub|
pub.resources :users, :has_many => [ :favourites ]
pub.resources :favourites
pub.resources :assets, :only => [ :show ]
end
Unfortunately the url is failing to generate for the feed.entry line:
feed.entry(favourite, :url => favourite.asset.external_ref) do |entry|
The error is "undefined method `favourite_url' for ActionView::Base".
I've tried changing the feed.entry line to:
feed.entry([:public, favourite], :url => favourite.asset.external_ref) do |entry|
But this then returns the entry for an Array rather than a favourite! Someone had a similar problem here also.
I know that adding the line:
map.resource :favourites
to my routes.rb would 'fix' this problem but this resource is only available nested beneath the /public namespace.
Has anyone had this problem before?
Cheers
Arfon
Just to follow up. Based upon Michael's suggestion I'm passing the full url param and this seems to generate the correct url for the feed.entry line.
#favourites.each do |favourite|
feed.entry(favourite, :url => public_user_favourite_url(:id => favourite, :user_id => #user)) do |entry|
entry.title(favourite.asset.external_ref)
entry.content(image_tag(favourite.asset.location), :type => 'html')
entry.author do |author|
author.name(#user.zooniverse_user_id)
end
end
end
You are using favourite.asset.external_ref as the title of the entry, which leaves me to believe the URL for that entry should probably be defined as:
public_user_favourite_url(:id => favourite, :user_id => #user)
Which, if favorite.id = 9 and #user.id = 1, would generate:
http://localhost:3000/public/users/1/favourites/9
Is this what you are looking for?
Related
I have my attachment model related to test_suite model. I'm able to upload a file to my database. I was able to seen the content of my uploaded files. I implemented a serve method and the URL I got when I want to see the file is correct. However, I'm getting "Couldn't find Attachment with 'id'= "
controllers/attachment_controller.rb:
def serve
#attachment = Attachment.find(params[:id]) # this is the line generating the error
send_data(#attachment.file_contents, :filename => "#{#attachment.attach_file_name}",
:type => #attachment.attach_content_type,
:size => #attachment.attach_file_size,
:disposition => "inline")
end
routes.rb:
resources :test_suites do
resources :attachments, :only => [:create, :new, :destroy,:show] do
get "serve" # since serve is not a restfull route, it need to be under it's resource
end
end
model/attachment.rb:
class Attachement < ApplicationRecord
belongs_to :test_suite
has_attached_file :attach
validates_attachment_content_type :attach, :content_type => ["text/xml", "text/plain","text/html"], :message => 'File must be txt, xml, or html'
# create a function that sets the uploadedFile object attributes to our newly created file.
def attach=(attach)
# read allows us to process the data and read from it
self.file_contents = attach.read
self.attach_file_name = attach.original_filename
self.attach_content_type = attach.content_type
self.attach_file_size = attach.size
end
end
views/test_suites/show.html.erb:
<% test_suite.attachments.each do |attachment| %>
<p><%= link_to attachment.attach_file_name.split('.').first, test_suite_attachement_serve_path(test_suite,attachment)%> </p>
<% end %>
Add it in member block
resources :test_suites do
resources :attachments, :only => [:create, :new, :destroy,:show] do
member do
get :serve # since serve is not a restfull route, it need to be under it's resource
end
end
end
This will generate following route for you
/test_suites/attachments/:id/serve
I'm a front end guy getting more and more into using sinatra. I'm building an app currently and am trying to find an elegant way to DRY up the routes in my myapp.rb file.
Currently I have these routes:
get '/' do
haml :content
end
get '/content' do
haml :content, :layout => :empty
end
get '/show_layout' do
haml :show_layout
end
get '/conversion' do
haml :conversion, :layout => :empty
end
get '/optout' do
haml :optout, :layout => false
end
get '/terms' do
haml :terms, :layout => :empty
end
With regards to the templates, I know I can do something like this to combine them:
get '/:name' do
haml params[:name].to_sym
end
But what about the layouts? There are only 2 layouts here, layout.haml and empty.haml (:layout, and :empty), 3 if you count :layout => false
Is this something that is best done using a hash? Something like:
layout_map = {
"" => "",
:content => "",
:show_layout => "",
:conversion => :empty,
:optout => false,
:terms => :empty
}
get '/:name' do
haml params[:name].to_sym, :layout => layout_map[:name]
end
Seems like its on the right track but I can't get it to work properly.
Thanks for all your help.
You can use your:
get '/:name' do
haml params[:name].to_sym
end
plus a before route that will set your layout:
before '/:name' do
layout_map = {
:content => "",
:show_layout => "",
:conversion => :empty,
:optout => false,
:terms => :empty
}
set :layout => layout_map[params[:name]]
end
This will set your layout according to params[:name] with every call. But be careful with .simming every route. If someone calls many 404s you create lots and lots of dead symbols which are not garbage collected and eventually will crash your app. Better do this:
get '/:name' do
halt 404 unless File.exist?("views/#{params[:name]}.haml")
time = File.stat("views/#{params[:name]}.haml").ctime
last_modified(time)
haml params[:name].intern
end
This will only create a symbol for params[:name] if there is a file with that name. So you're on the safe side because the symbol already exists.
Thanks for all your help everyone. Ended up going with this as my solution due to some other requirements of the app.
get "/:base_route/" do
haml :"#{params[:base_route]}/content", :layout => :"#{params[:base_route]}/layout"
end
get "/:base_route/:name" do
layout_map = {
:landing => :layout,
:content => :empty,
:show_layout => :layout,
:conversion => :empty,
:terms => :empty
}
haml :"#{params[:base_route]}/#{params[:name]}", :layout => :"#{params[:base_route]}/#{layout_map[params[:name].to_sym]}"
end
HI Everyone ,
I have rails admin implemented in my project Now there are couple of thing that I currently stuck at
I want a link (Mark as Publisher) In the list View of my user Controller in the rails admin as ajax link something that is done using remote => true in rails after that where the write the associated jscode and html code for it
for the above custom action "mark_as_publisher" I define the configuration setting like this
Inside config/rails_admin.rb
config.actions do
# root actions
dashboard # mandatory
# collection actions
index # mandatory
new
export
history_index
bulk_delete
# member actions
show
edit
delete
history_show
show_in_app
member :mark_as_publisher
end
Now The Definition of the custom action look like this
require "rails_admin_mark_as_publisher/engine"
module RailsAdminMarkAsPublisher
end
require 'rails_admin/config/actions'
module RailsAdmin
module Config
module Actions
class MarkAsPublihser < Base
RailsAdmin::Config::Actions.register(self)
register_instance_option :collection do
true
end
register_instance_option :http_methods do
[:get,:post]
end
register_instance_option :route_fragment do
'mark_as_publisher'
end
register_instance_option :controller do
Proc.new do
binding.pry
if request.get?
respond_to do |format|
format.html { render #action.template_name}
end
elsif request.post?
redirect_path = nil
if #object.update_attributes(:manager => true)
flash[:success] = t("admin.flash.successful", :name => #model_config.label, :action => t("admin.actions.mark_as_publisher.done"))
redirect_path = index_path
else
flash[:error] = t("admin.flash.error", :name => #model_config.label, :action => t("admin.actions.mark_as_publisher.done"))
redirect_path = back_or_index
end
end
end
end
end
end
end
end
Now the View for the same define in app/view/rails_admin/main/mark_as_publisher.erb look like this
<%= rails_admin_form_for #object, :url => mark_as_publisher_path(:model_name => #abstract_model.to_param, :id => #object.id), :as => #abstract_model.param_key,:method => :post ,:html => { :class => "form-horizontal denser", :data => { :title => "Mark" } } do |form| %>
<%= form.submit "save" %>
<%end%>
The get and post url for mark_as_publisher does come under by controller define above and saving the above form result in error called
could not find routes for '/user/5/mark_as_publisher' :method => "post"
Does Any body has an idea of what I'm missing
Sorry for the delayed reply, but I also came into the exact same issue.
EDIT: I notice you already have this, have you tried restarting your server?
if you add the following it will fix it.
register_instance_option :http_methods do
[:get,:post]
end
The problem is by default Actions only respond to the :get requests.
If you run
rake routes
You will see something along the lines of
mark_as_publisher_path GET /:model_name/:id/mark_as_publisher(.:format) rails_admin/main#mark_as_publisher
https://github.com/sferik/rails_admin/blob/master/lib/rails_admin/config/actions/base.rb#L89
I'm trying to use a named route with {{id}} as one of the params, allowing the rendered content to be consumed by Handlebars. url_for is escaping the param so the resulting url contains %7B%7Bid%7D%7D. I've tried adding :escape => false to the call, but it has no effect.
routes.rb
resources :rants, :except => [:show] do
post '/votes/:vote', :controller => 'votes', :action => 'create', :as => 'vote'
end
index.haml
%script{:id => 'vote_template', :type => 'text/x-handlebars-template'}
.votes
= link_to 'up', rant_vote_path(:rant_id => '{{id}}', :vote => 'up')
%span {{votes}}
= link_to 'down', rant_vote_path(:rant_id => '{{id}}', :vote => 'down')
application.js
var vote_template = Handlebars.compile($('#vote_template').html());
output
<script id="vote_template" type="text/x-handlebars-template">
<div class='votes'>
up
<span>{{votes}}</span>
down
</div>
</script>
I've simplified the example for the sake of readability but question remains the same; is there any way to use a named route with {{ }} as a param? I understand I can just do link_to 'up', '/rants/{{id}}/votes/up' so please don't supply that as an answer.
The trouble is the mustache characters are not valid in URLs and are being escaped. I'd recommend creating a wrapper.
def handlebar_path(helper, arguments={})
send("#{helper}_path", arguments).gsub(/%7B%7B(.+)%7D%7D/) do
"{{#{$1}}}"
end
end
handlebar_path :rant_vote, :rant_id => '{{id}}', :vote => 'up'
I ended up just overriding url_for to handle a custom param:
module TemplateHelper
def url_for(*args)
options = args.extract_options!
return super unless options.present?
handle = options.delete(:handlebars)
url = super(*(args.push(options)))
handle ? url.gsub(/%7B%7B(.+)%7D%7D/){|m| "{{#{$1}}}"} : url
end
end
So now, calling named_url(:id => '{{id}}', :handlebars => true) works as you'd expect.
I'd like my website to have URLs looking like this:
example.com/2010/02/my-first-post
I have my Post model with slug field ('my-first-post') and published_on field (from which we will deduct the year and month parts in the url).
I want my Post model to be RESTful, so things like url_for(#post) work like they should, ie: it should generate the aforementioned url.
Is there a way to do this? I know you need to override to_param and have map.resources :posts with :requirements option set, but I cannot get it all to work.
I have it almost done, I'm 90% there. Using resource_hacks plugin I can achieve this:
map.resources :posts, :member_path => '/:year/:month/:slug',
:member_path_requirements => {:year => /[\d]{4}/, :month => /[\d]{2}/, :slug => /[a-z0-9\-]+/}
rake routes
(...)
post GET /:year/:month/:slug(.:format) {:controller=>"posts", :action=>"show"}
and in the view:
<%= link_to 'post', post_path(:slug => #post.slug, :year => '2010', :month => '02') %>
generates proper example.com/2010/02/my-first-post link.
I would like this to work too:
<%= link_to 'post', post_path(#post) %>
But it needs overriding the to_param method in the model. Should be fairly easy, except for the fact, that to_param must return String, not Hash as I'd like it.
class Post < ActiveRecord::Base
def to_param
{:slug => 'my-first-post', :year => '2010', :month => '02'}
end
end
Results in can't convert Hash into String error.
This seems to be ignored:
def to_param
'2010/02/my-first-post'
end
as it results in error: post_url failed to generate from {:action=>"show", :year=>#<Post id: 1, title: (...) (it wrongly assigns #post object to the :year key). I'm kind of clueless at how to hack it.
Pretty URLs for Rails 3.x and Rails 2.x without the need for any external plugin, but with a little hack, unfortunately.
routes.rb
map.resources :posts, :except => [:show]
map.post '/:year/:month/:slug', :controller => :posts, :action => :show, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/
application_controller.rb
def default_url_options(options = {})
# resource hack so that url_for(#post) works like it should
if options[:controller] == 'posts' && options[:action] == 'show'
options[:year] = #post.year
options[:month] = #post.month
end
options
end
post.rb
def to_param # optional
slug
end
def year
published_on.year
end
def month
published_on.strftime('%m')
end
view
<%= link_to 'post', #post %>
Note, for Rails 3.x you might want to use this route definition:
resources :posts
match '/:year/:month/:slug', :to => "posts#show", :as => :post, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/
Is there any badge for answering your own question? ;)
Btw: the routing_test file is a good place to see what you can do with Rails routing.
Update: Using default_url_options is a dead end. The posted solution works only when there is #post variable defined in the controller. If there is, for example, #posts variable with Array of posts, we are out of luck (becase default_url_options doesn't have access to view variables, like p in #posts.each do |p|.
So this is still an open problem. Somebody help?
It's still a hack, but the following works:
In application_controller.rb:
def url_for(options = {})
if options[:year].class.to_s == 'Post'
post = options[:year]
options[:year] = post.year
options[:month] = post.month
options[:slug] = post.slug
end
super(options)
end
And the following will work (both in Rails 2.3.x and 3.0.0):
url_for(#post)
post_path(#post)
link_to #post.title, #post
etc.
This is the answer from some nice soul for a similar question of mine, url_for of a custom RESTful resource (composite key; not just id).
Ryan Bates talked about it in his screen cast "how to add custom routes, make some parameters optional, and add requirements for other parameters."
http://railscasts.com/episodes/70-custom-routes
This might be helpful. You can define a default_url_options method in your ApplicationController that receives a Hash of options that were passed to the url helper and returns a Hash of additional options that you want to use for those urls.
If a post is given as a parameter to post_path, it will be assigned to the first (unnassigned) parameter of the route. Haven't tested it, but it might work:
def default_url_options(options = {})
if options[:controller] == "posts" && options[:year].is_a?Post
post = options[:year]
{
:year => post.created_at.year,
:month => post.created_at.month,
:slug => post.slug
}
else
{}
end
end
I'm in the similar situation, where a post has a language parameter and slug parameter. Writing post_path(#post) sends this hash to the default_url_options method:
{:language=>#<Post id: 1, ...>, :controller=>"posts", :action=>"show"}
UPDATE: There's a problem that you can't override url parameters from that method. The parameters passed to the url helper take precedence. So you could do something like:
post_path(:slug => #post)
and:
def default_url_options(options = {})
if options[:controller] == "posts" && options[:slug].is_a?Post
{
:year => options[:slug].created_at.year,
:month => options[:slug].created_at.month
}
else
{}
end
end
This would work if Post.to_param returned the slug. You would only need to add the year and month to the hash.
You could just save yourself the stress and use friendly_id. Its awesome, does the job and you could look at a screencast by Ryan Bates to get started.