How can I include rails code inside a validation message? - ruby-on-rails

validates_presence_of :match,
:message => "for your name and password could not be found. Did you #{link_to "forget your password", help_person_path}?"
That's what I want to do. I can interpolate variables that are set in the model, but using simple Rails code like "link_to" doesn't seem to work. Which is a bummer.

You can't and shouldn't call the link_to method from your models. However, you can get access to named routes like this:
class PostHack
include Rails.application.routes.url_helpers
end
class Post < ActiveRecord::Base
validates_presence_of :match,
:message => "<a href='#{PostHack.new.send :admin_posts_path}'>My link</a>"
end
Very hackish. I hope you will not use it.

Related

Using slugs in polymorphic_path

Is there a way I can use parameters in a polymorphic_path, to pass in a slug?
For instance, I have the following routes
routes.rb
MyApp::Application.routes.draw do
match "movies/:slug" => 'movies#show', :as=>:movie
match "series/:slug" => 'series#show', :as=>:series
end
And I have the following models:
Movie.rb
class Movie < ActiveRecord::Base
has_many :cast_members, :as=>:media_item
end
Series.rb
class Series < ActiveRecord::Base
has_many :cast_members, :as=>:media_item
end
CastMember.rb
class CastMember < ActiveRecord::Base
belongs_to :media_item, :polymorphic=>true
end
This works great, and I can reference my movie from the cast member, and vice-versa, just like a normal has_many/belongs_to relationship.
I can also do this from within my cast_member view:
*cast_members/show.html.erb*
link_to (#cast_member.movie.title, movie_path(#cast_member.movie.slug))
which returns "movie/movie-title"
and I can do
*cast_members/show.html.erb*
link_to (#cast_member.movie.title, polymorphic_path(#cast_member.media_item))
but this returns "/movies/24"
I've tried passing a slug as an item to polymorphic_path in different ways, like
link_to (#cast_member.movie.title, polymorphic_path(#cast_member.media_item, #cast_member.media_item.slug))
link_to (#cast_member.movie.title, polymorphic_path(#cast_member.media_item, :slug=>#cast_member.media_item.slug))
link_to ([#cast_member.movie.title, polymorphic_path(#cast_member.media_item, #cast_member.media_item.slug]))
but these all return errors or the path with the id.
How can I make the polymorphic_path use the movie.slug instead of the id?
I switched over to using friendly_id to generate slugs. It magically handles all the slug<->id conversions magically in the background, and sosolves the issue.
I do think that rails should have a baked-in way to do this, the same way you can pass a slug into the default *_path methods.
I solved this by using Rails' built-in path helpers instead of polymorphic_path. I really wanted to use that method, since it required use of the model's ID, I couldn't.
In my app, I have a lot of models that are "slugable", so it made sense to include a #to_path method in the slugable mixin.
# app/models/concerns/slugable.rb
module Slugable
extend ActiveSupport::Concern
included do
validates :slug, presence: true, uniqueness: {case_sensitive: false}
end
def to_path
path_method = "#{ActiveModel::Naming.singular_route_key(self)}_path"
Rails.application.routes.url_helpers.send(path_method, slug)
end
def slug=(value)
self[:slug] = value.blank? ? nil : value.parameterize
end
end
then in the templates:
<%= link_to my_slugable_model.name, my_slugable_model.to_path %>
If you have nested resources in your routes, then you'll need to adjust the code for that resource.
something like this (untested):
def to path(my_has_many_model_instance)
class_path = self.class.to_s.underscore
has_many_class_path = my_has_many_model_instance.class.to_s.underscore
path_method = "#{self_path}_#{has_many_class_path}_path"
Rails.application.routes.url_helpers.send(path_method, slug, my_has_many_model)
end
Good luck!

rails custom validation on action

I would like to know if there's a way to use rails validations on a custom action.
For example I would like do something like this:
validates_presence_of :description, :on => :publish, :message => "can't be blank"
I do basic validations create and save, but there are a great many things I don't want to require up front. Ie, they should be able to save a barebones record without validating all the fields, however I have a custom "publish" action and state in my controller and model that when used should validate to make sure the record is 100%
The above example didn't work, any ideas?
UPDATE:
My state machine looks like this:
include ActiveRecord::Transitions
state_machine do
state :draft
state :active
state :offline
event :publish do
transitions :to => :active, :from => :draft, :on_transition => :do_submit_to_user, :guard => :validates_a_lot?
end
end
I found that I can add guards, but still I'd like to be able to use rails validations instead of doing it all on a custom method.
That looks more like business logic rather than model validation to me. I was in a project a few years ago in which we had to publish articles, and lots of the business rules were enforced just at that moment.
I would suggest you to do something like Model.publish() and that method should enforce all the business rules in order for the item to be published.
One option is to run a custom validation method, but you might need to add some fields to your model. Here's an example - I'll assume that you Model is called article
Class Article < ActiveRecord::Base
validate :ready_to_publish
def publish
self.published = true
//and anything else you need to do in order to mark an article as published
end
private
def ready_to_publish
if( published? )
//checks that all fields are set
errors.add(:description, "enter a description") if self.description.blank?
end
end
end
In this example, the client code should call an_article.publish and when article.save is invoked it will do the rest automatically. The other big benefit of this approach is that your model will always be consistent, rather than depending on which action was invoked.
If your 'publish' action sets some kind of status field to 'published' then you could do:
validates_presence_of :description, :if => Proc.new { |a| a.state == 'published' }
or, if each state has its own method
validates_presence_of :description, :if => Proc.new { |a| a.published? }

Rails equivalent to Django's "choices"

I know there is no real equivalent in Rails but my question is mostly about best practice...
In Django, if you want to limit a model field to a limited set of choices, you would do something like this (in your model):
COLOR_CHOICES = (('B', 'Blue'), ('R', 'Red'))
item_color = models.CharField(choices=COLOR_CHOICES)
From my (basic) understanding of Rails, I can achieve something similar, for example, by using a select tag in the forms dealing with adding/editing that model...
My question however is, where would it be appropriate to declare the "choices" hash (again I'm guessing here that a hash is what I need?). Basically I just want it to be easily re-usable in any forms where I might need to present those choices, and when it comes to validating at the model level.
Any help/tips would be appreciated!
On the validation side of things, probably validates_inclusion_of is what you need:
class Coffee < ActiveRecord::Base
validates_inclusion_of :size, :in => %w(small medium large),
:message => "%{value} is not a valid size"
end
As for generating the helper, you can try something like:
class Coffee < ActiveRecord::Base
##coffe_size = %w(small medium large)
validates_inclusion_of :size, :in => ##coffe_size,
:message => "%{value} is not a valid size"
def self.coffee_size_options
##coffe_size.map{ |z| [z,z]}
end
end
And then in some helper:
<%= select(:coffee, :size, Coffee.coffee_size_options) %>
You can simply use enum
class Coffee < ActiveRecord::Base
enum color: [ :blue, :red, :green ]
end
More information here : https://api.rubyonrails.org/v5.2.4.1/classes/ActiveRecord/Enum.html
2 years later, there's a better option: values_for
class Car < ActiveRecord::Base
attr_accessible :brand
values_for :brand, :has=>[:ford, :chevy, :dodge], :add=>[:constants]
def doStuff
# Now you can...
Car.brands # [:ford, :chevy, :dodge]
Car::BRAND_FORD # "ford"
myCar = Car.new(:brand=>Car::BRAND_FORD)
myCar.valid? # true
myCar.brand= "duck."
myCar.valid? # false
end
end

Creating permalinks (slugs) in Rails - why is my test failing?

I'm writing a small CMS as a Rails test project (also planning to use it for my personal website). I want SEO-friendly URLs so I have a test to verify that permalinks are automatically being created based on a page's title (e.g. About Us => about-us). I can't figure out why this test is failing, however. Here's the code (I'm using Rails 2.3.2):
# page_test.rb
# note I am using the "shoulda" framework
require 'test_helper'
class PageTest < ActiveSupport::TestCase
should_validate_presence_of :title, :permalink, :content
should_validate_uniqueness_of :title
should "create permalink automatically" do
p = pages(:sample_page)
p.save
assert_equal "sample-page", p.permalink
end
end
# pages.yml
sample_page:
title: Sample Page
permalink: # gets automatically created by model
content: This is a sample page
# page.rb
class Page < ActiveRecord::Base
validates_presence_of :title, :permalink, :content
validates_uniqueness_of :title
before_save :generate_permalink
private
def generate_permalink
self.permalink = self.title.parameterize
end
end
What happens is that the permalink is nil, instead of "sample-page" like it's supposed to be. It works, however, if I manually put the permalink in the fixture and change the test around, for example:
p - pages(:sample_page)
p.title = "Contact Us"
p.save
assert_equal "contact-us", p.permalink
I could fix it like this, but I'm wondering why it's not firing the before_save method for the original test.
Alright, I was able to figure it out. I needed to use before_validation as the callback and not before_save
Does it work if you remove the empty permalink: key from your pages.yml file?

Is it legal to stub the #class method of a Mock object when using RSpec in a Ruby on Rails application?

I would like to stub the #class method of a mock object:
describe Letter do
before(:each) do
#john = mock("John")
#john.stub!(:id).and_return(5)
#john.stub!(:class).and_return(Person) # is this ok?
#john.stub!(:name).and_return("John F.")
Person.stub!(:find).and_return(#john)
end
it.should "have a valid #to field" do
letter = Letter.create!(:to=>#john, :content => "Hello John")
letter.to_type.should == #john.class.name
letter.to_id.should == #john.id
end
[...]
end
On line 5 of this program, I stub the #class method, in order to allow things like #john.class.name. Is this the right way to go? Will there be any bad side effect?
Edit:
The Letter class looks like this:
class Letter < ActiveRecord::Base
belongs_to :to, :polymorphic => true
[...]
end
I wonder whether ActiveRecord gets the :to field's class name with to.class.name or by some other means. Maybe this is what the class_name method is ActiveRecord::Base is for?
I think you should be using mock_model for this particular case.
Your before(:each) would look like that:
before(:each) do
#john = mock_model(Person, :name => "John F.")
Person.stub!(:find).and_return(#john)
end
Then for your other question, you should not really care about how Rails works to test your behaviour. I don't think it's a good idea to test to_type and to_id fields yourself. This is Rails behaviour and as such should be tested in Rails, not in your project.
I have been using Remarkable for a while and it makes this really easy to specify:
describe Letter
should_belong_to :to, :polymorphic => true
end

Resources