I have a notices model which contains records of notices for each user. Each notice record contains a message which is a text field with the notice message. These messages look like:
"#{current_user.username} liked your photo '#{#photo.name}."
In the example above, I would like the user and the photo to also be hyperlinks to that user and photo.
Here is a snippet from my likes_controller which generates a notice when a new like is created:
class LikesController < ApplicationController
def create
#photo = Photo.find(params[:id])
#like = Like.new(:photo_id => #photo.id, :user_id => current_user.id)
if #like.save
#notice = Notice.new(:user_id => #photo.user_id, :message => "#{current_user.username} liked your photo '#{#photo.name}'
end
Any thoughts on how I can include links in the message; is this even possible? Thanks.
Adding a link to your message is a rendering issue. In my opinion, you're rendering the message too soon, I would render it in the view.
If you change your Notice model so that it contains a the user_id and the like_id, you can render the notice text in the view (which also lets you localize the text later, should it prove necessary).
Rendering in the view lets you use the standard link_to helper to generate your links.
Related
I want to store pages in the database that are dynamically added/edited/etc. Along with the page title and content I also supply which layout to use and a controller name and view name in order to support the instantiation of models which the view (e.g. text field in the pages table) will use.
I'm having trouble rendering the text from the database and having the layout still be used.
class AboutController < BaseController
def index
#model_data = ...
render_dynamic_page("about", "index")
end
end
class BaseController < ApplicationController
layout "public"
def render_dynamic_page(controller_name, action_name)
page = Page.where("`controller_name` = :controller_name AND `action_name` = :action_name", { :controller_name => controller_name, :action_name => action_name }).first
render :layout => page.layout_name, :text => page.content
end
end
I'm using :text here and I've also tried :inline but both seem to not render the content within the layout.
How can I accomplish this?
Normally if you want to allow users to edit the html pages in a rails application, and store the pages in a DB, the standard way is to use the template language called liquid.
It allows you to define dynamic content, ideal for CMS systems in rails, I think its better you have a look first.
following are some important links
liquid home page
rails cast about liquid
git hub page
and there are lots of resources in SO itself. :)
I was so close - I just had to switch the order of the parameters to the render method:
render :inline => page.content, :layout => (page.layout_name || "public")
I also added a default layout to use public in case a page.layout_name was not specified in the database. The other thing that's worth mentioning is the difference between :text and :inline - only :inline seems to actually "process" the content as if it were an ERB whereas the :text option just literally outputs the raw text.
Note : I ended up creating an ERB for each page using ERB.new(page.content), caching the list of dynamic ERBs and then pulling the pre-rendered ERB from the cache and displaying it using:
render :inline => the_cached_erb.result(binding), :layout => (page.layout_name || "public")
There is still some weird behavior going on but I think it will work in the end.
Just FYI and for greater tools variety you may want to check out Mercury editor:
http://jejacks0n.github.io/mercury/
https://github.com/jejacks0n/mercury
http://railscasts.com/episodes/296-mercury-editor
In my rails application I've created a business daily report. There is some non-trivial logic for showing it (all kind of customizable parameters that are used for filtering in the model, a controller that calls that model and some non-trivial view for it, for example, some of the columns are row-spanning over several rows).
Now I wish to send this report nightly (with fixed parameters), in addition to the user ability to generate a customize report in my web site. Of course, I wish not to re-write/duplicate my work, including the view.
My question is how can I call the controller action from my mailer so that it will be as if the page was requested by a user (without sending a get request as a browser, which I wish to avoid, of course)?
In answer to your question is if you are generating some sort of pdf report then go with using the wicke_pdf gem does exactly that generates pdfs. To send a report on a nightly basis the best thing for this is to implement some sort of cron job that runs at a particular time which you can do using the whenever gem. You can do something like:
schedule.rb
every :day, :at => '12:00am'
runner User.send_report
end
With this at hand you can see that you call the send_report method sits inside the User model class as shown below:
User.rb
class User < ActiveRecord::Base
def self.send_report
ReportMailer.report_pdf(#user).deliver
end
end
Inside send_report we call the mailer being ReportMailer which is the name of the class for our mailer and the method being report_pdf and pass in the user. BUT remember this is an example I have here I am not sure the exact specified information you want in a report.
Mailer
class ReportMailer< ActionMailer::Base
default :from => DEFAULT_FROM
def report_pdf(user)
#user = user
mail(:subject => "Overtime", :to => user.email) do |format|
format.text # renders report.text.erb for body of email
format.pdf do
attachments["report.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "report",:template => 'report/index.pdf.erb',
:layouts => "pdf.html"))
end
end
end
end
Inside the mailer there are a variety of things going on but the most important part is inside the format.pdf block that uses a variety of wicked_pdf methods (this is assuming that you are using wicked_pdf btw. Inside the block you create a new WickedPDF pdf object and render it to a string. Then provide it with the name of the report, the template and the layout. It is important that you create a template. This usually will where the report will be displaying from. The file type is a .pdf.erb this means that when this view or report is generated in the view the embedded ruby tags are being parsed in and the output is going to be a pdf format.
UserController
def report
#user = User.scoped
if params[:format] == 'pdf'
#Do some stuff here
User.send_report(#users)
end
respond_to do |format|
format.html
format.pdf do
render :pdf => "#{Date.today.strftime('%B')} Report",
:header => {:html => {:template => 'layouts/pdf.html.erb'}}
end
end
end
The key thing you asked that I picked up on.
how can I call the controller action from my mailer
In the controller simply collate a scope of Users, then check the format is a pdf, providing it is do some stuff. Then it will run the method send_report which I earlier highlighted in the user model class (Btw in your words this is the controller calling the model). Then inside the respond block for this there is a format.pdf so that you can generate the pdf. Once again note that you need a template for the core design of the pdf, which is similar to how rails generates an application.html.erb in the layouts. However here we have a pdf.html.erb defined. So that this can be called anywhere again in your application should you want to generate another pdf in your application somewhere else.
Think I've provided a substantial amount of information to set you off in the right direction.
I have a posting model that has tags using the rocket_tag gem
class Posting < ActiveRecord::Base
attr_taggable :tags
def tag_list
self.tags.join(",")
end
def tag_list=(new_tags)
attribute_will_change!(:tag_list)
# split into array (comma and any spaces), ignore empties
self.tags = new_tags.split(/,[\s]*/).reject(&:empty?)
end
It seems to work fine in my dev environment but when I use FactoryGirl to generate a posting for tests it doesn't seem to add the tags to the search index so I assume these are getting saved after the posting and so when the search index gets updated it doesn't see any saved tags so they are not searchable using tire.
I assume this means that I need to add an after_save callback to the rocket_tag Tag model to call touch() against the posting model but I'm not sure how to extend the model from the gem to add this extra callback and method to it.....unless something from the above could be at fault.
FactoryGirl.define do
factory :posting do
sequence(:name) { |m| "Posting #{m} name" }
tag_list "tag,another,third"
user
end
end
Not sure why it doesn't work but in the end I used FactoryGirl.create to create the posting, visited the edit page for the posting, used capybara's fill_in to add the tags, click_button "Submit" and then I refreshed the search index.
ie I added the tags in the same way a normal webpage user would rather than trying to use FactoryGirl to set them.
I have made a resource.
resources :dashboards
I have a partial file which contains a form and I want to use this partial (as the form elements won't change) to update and create. So here is what I have:
Controller
class DashboardsController < ApplicationController
def new
#dashboard = Dashboard.new
end
end
View
/dashboards/new.html.erb
<%= render :partial => "form", :locals => { :dashboard => #dashboard } %>
Partial Form
/dashboards/_form.html.erb
<%= form_for(#dashboard) do |form| %>
.....
<% end %>
Ruby Guide
The Ruby Guide states:
The Article model is directly available to users of the application, so — following the best practices for developing with Rails — you should declare it a resource. When dealing with RESTful resources, calls to form_for can get significantly easier if you rely on record identification. In short, you can just pass the model instance and have Rails figure out model name and the rest. For example:
## Creating a new article
# long-style:
form_for(#article, :url => articles_path)
# same thing, short-style (record identification gets used):
form_for(#article)
## Editing an existing article
# long-style:
form_for(#article, :url => article_path(#article), :html => { :method => "put" })
# short-style:
form_for(#article)
Result
I thought I have followed the Rails Guide correctly. Because I made #dashboard a resource. I could just pass it into the form and have it handle the action, method and the rest. Instead I'm getting this:
<form accept-charset="UTF-8" action="/dashboards" class="new_dashboard" id="new_dashboard_" method="post">
According to the docs. Shouldn't the action of my form now be "/dashboards/new" because we are on the new action? And should it be passing an extra field declaring the method to be put when I use the same code in the /edit action??
My result is always the same no matter what. The form never changes.
What am I doing wrong?
EDIT
Here is my router info from rake routes
GET /dashboards(.:format) dashboards#index
POST /dashboards(.:format) dashboards#create
GET /dashboards/new(.:format) dashboards#new
GET /dashboards/:id/edit(.:format) dashboards#edit
GET /dashboards/:id(.:format) dashboards#show
PUT /dashboards/:id(.:format) dashboards#update
DELETE /dashboards/:id(.:format) dashboards#destroy
You are correct that you should be able to "pass #dashboard into the form and have it handle the action, method and the rest." The issue here is what new is in the context of RESTful actions.
When you declare a set of resources with resources :dashboards, you are creating a set of routes which map requests to controller actions:
GET /dashboards index
GET /dashboards/new new
POST /dashboards create
GET /dashboards/:id show
GET /dashboards/:id/edit edit
PUT /dashboards/:id update
DELETE /dashboards/:id destroy
You can check this if you run rake routes.
The issue here is that the new action is defined as a GET request to the path /dashboards/new, i.e. this is the route for the form itself. The URL in the action attribute of the actual form is something else: this is where the form will post the data to with a POST request, which on the server (rails) side will map to the create controller action.
When you use the form helper with form_for(dashboard), a form is created with a route corresponding to what dashboard is: if it is a new record (i.e. it does not yet exist in the database), then the form action will be create (and point to /dashboards), whereas if it already exists it will point to the actual URL for the record (e.g. /dashboards/123). This is what makes the form helpers so useful.
So, to sum up, /dashboards is the correct URL, not for the new action but for the create action, which the form helper uses because dashboard is a new record. new is the route to the page where the form resides, i.e. /dashboards/new.
Hope that makes sense.
p.s. as a side note, you shouldn't be accessing #dashboard in the partial if you are passing it in as a local (:locals => { :dashboard => #dashboard }). Just use dashboard.
For certain models, I wish to provide functionality that allows a user to create a new record with default attributes based on copy of an existing record.
I'm wondering what would be the correct restful route for this.
My initial thinking is that it could be a parameter to the new action. I.e. to borrow from the the Rails Guides examples, instead of just:
GET : /photos/new
Also allow:
GET : /photos/new/:id
...where :id is the id of the record to use as a template. The response would be a new/edit form, same as with a plain old new but the values would be pre-filled with data from the existing record. The parameter (or absense of it) could be easily handled by the new controller method.
The alternative seems to be to create a new controller method, for example copy which would also accept an id of an existing record and response with the new form as above. This seems a little 'incorrect' to me, as the record is not actually being copied until the user saves the new record (after probably editig it somewhat).
TIA...
UPDATE: my question is not "how do I do this in rails?", it's "is it RESTful?"
my question is not "how do I do this in rails?", it's "is it RESTful?"
No, it isn't. For that matter, neither is GET /photos/new. Rails seems to be hopelessly mired in the past, where it was considered haute programme for a GET on a URI to return an HTML form which would then POST x-www-form-urlencoded data back to that same URI. The opacity of that POST forces them to invent new verbs-as-URI's like /photos/new, when you could be using PUT instead, or at least POST with the same media type.
The simplest way to make a copy of an HTTP resource RESTfully is:
GET /photos/{id}/ -> [representation of a photo resource]
...make modifications to that representation as desired...
POST /photos/ <- [modified representation]
If you're implementing this for browsers, you should be able to perform those actions via Ajax quite easily, using an HTML page sitting perhaps at /photos/manager.html/ to drive the interaction with the user.
You can try to use nested resources. I'm not exactly sure about structure of you application, but in general using nested photos will look somehow like this:
routes.rb
resources :photos do
resources :photos
end
photos_controller.rb
before_filter :find_parent_photo, :only => [:new, :create]
def create
#photo = Photo.new params[:photo]
if #parent_photo.present?
# fill some #photo fields from #parent_photo
end
#photo.save
respond_with #photo
end
def find_parent_photo
#parent_photo = Photo.find(params[:photo_id]) if params[:photo_id].present?
end
new.html.haml
= form_for [#parent_photo, #photo] do |f|
-# your form code
previously when you wanted to add a link to photo creation you wrote something like that
= link_to "new photo", [:new, :photo]
now if you want to add a link to photo creation based on foto #photo1
= link_to "new photo based on other one", [:new, #photo1, :photo]
You should be able to match a route like so:
match 'photos/new/:photo_id' => 'photos#new
or you could just pass a :photo_id parameter in the url and handle it in the controller:
'/photos/new?photo_id=17'
Example using helper method: new_photo_path(:photo_id => 17)
Edit: I don't know if this conforms to REST
It may be over the top, but you could do something like this:
class PhotoCopiesController < ApplicationController
def new
#photo = Photo.find(params[:photo_id]).dup
end
def create
end
end
and
resources :photo_copies, :only => [:new, :create]
and
= link_to 'Copy', photo_copy_path(:photo_id => #photo.id)