friendly_id and ActiveScaffold conflict - ruby-on-rails

I have friendly_id and ActiveScaffold installed for my Rails application.
Because not all of my models have unique name fields I have to use the Slugged Model to make it work. friendly_id does the job flawlessly I have friendly URLs and I can load the objects using the friendly id.
But when I want to create a new object with ActiveScaffold, it says the following error message:
ActiveScaffold::ReverseAssociationRequired
(Association slugs: In order to
support :has_one and :has_many where
the parent record is new and the child
record(s) validate the presence of the
parent, ActiveScaffold requires the
reverse association (the belongs_to).)
Of course I cannot create the belongs_to association in that side because it's created by the friendly_id module and every model which works slugged way should be included there.
The model looks like this:
class FooBar < ActiveRecord::Base
has_friendly_id :name, :use_slug => true, :approximate_ascii => true
end
In my ApplicationController:
class Admin::FooBarsController < Admin::ApplicationController
active_scaffold :foo_bar do |config|
config.list.columns = [ :id, :name ])
config.update.columns = [ :name ]
config.create.columns = config.update.columns
end
end
Is there a way to make this work?
The versions: friendly_id 3.2.0, ActiveScaffold latest in the rails-2.3 git branch.
UPDATE: Seems like it does not conflict in production mode.

calling
has_friendly_id :name, :cache_column => 'cached_slug', :use_slug => true
... creates a has_many and a has one associations pointing to a slug AR model which hasn't any polymorphic belongs to association properly defined.
So basically what you need to do to solve this error is to define the reverse associations in the controller of your parent model (the one who has friendly_id stuff)
active_scaffold :products do |config|
...
config.columns[:slug].association.reverse = :product
config.columns[:slugs].association.reverse = :product
end
and it works :-)
PS : I use friendly_id as gem and ActiveScaffold VHO master branch for rails 3

In the past I have the same problem , i have solved , but i dont remember my solution , lookin at my code the only relevant hack is to use friendly_id as plugin and load it at last with config.plugin in environemnt.rb
aviable_plugins = Dir.glob(RAILS_ROOT+"/vendor/plugins/*").collect {|i| i.split("/").last }
config.plugins = aviable_plugins + [:friendly_id] #friendly_id must be last
I'M NOT SURE ,sorry, but if you try let my know.
sorry for my english to

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 FriendlyId and normalize_friendly_id

Trying to get my app running the FriendlyId gem (version 4.0.1)
I think I'm doing this in the wrong order, but I want to strip out apostrophes before my friendly_id slug is generated when creating a new record. But I think the method normalize_friend_id is being called after the id is already generated.
I've added the following to my model:
class Team < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => :slugged
def normalize_friendly_id(string)
super.gsub("\'", "")
end
end
super calls the superclass first, meaning the friendly id is being generated then you're running gsub on THAT result. What you really want is to override this method completely.
Refer to: https://github.com/norman/friendly_id/blob/master/lib/friendly_id/slugged.rb#L244-246
your code should look like this:
def normalize_friendly_id(string)
string.to_s.gsub("\'", "").parameterize
end
or
def normalize_friendly_id(string)
super(string.to_s.gsub("\'", ""))
end
Hope that helps

Can't mass-assign protected attributes even if I use attr_accessible

I'm working on a Rails 3.2.2 application which has JSON APIs and I use a
CLI client for inserting some data. It works fine except for the Author
model. When I try to create a new post (Post belongs_to :author and
Author has_many :posts) I get the following error :
<h1>
ActiveModel::MassAssignmentSecurity::Error in PostsController#create
</h1>
<pre>Can't mass-assign protected attributes: name</pre>
I did lots of researches on the topic but I found no working solution
:-(
I use attr_accessible to avoid MassAssignent errors and it works for all
others models but not for the "Author" name attribute.
Here is the Author model :
class Author < ActiveRecord::Base
attr_accessible :name, :email
extend FriendlyId
friendly_id :name, use: :slugged
# some validations
has_many :posts
#authlogic
acts_as_authentic
# some stuffs
end
Actually, I have disabled whitelist_attributes and it solved my problem
but I suppose that it is not the convenient way to do this (and probably
not a good idea).
My questions are : Why the attr_accessible does not work here ? And how
can I solve the problem without disabling the whitelist ?
Thank you,
Revan
EDIT :
The method which creates the new post :
def create
#post = Post.new(params[:post])
#post.author = current_author
# respond to etc.
end
current_author finds the author using a given API Key.
I found the solution ! :-)
The problem was that I used acts_as_taggable_on_steroids plugin which does not work on Rails 3.2 ...
Since "Author" is the only model which has a :name attribute, I thought that the problem came from Author ... but the problem was in the Tag model (which is in the acts_as_taggable_on_steroid plugin). Indeed, its :name attribute is not "accessible".
So, I use the acts_as_taggable_on gem (https://github.com/mbleigh/acts-as-taggable-on)
which correctly works on Rails 3.x

how to index associated models using thinkingtank and indextank

We are using thinkingtank gem and having trouble indexing model associations, even simple ones. For example, a profile belongs to an institution, which has a name – we would like to do something like:
class Profile < ActiveRecord::Base
#model associations
define_index do
indexes institution(:name), :as => :institution_name
end
end
but that doesn't work. This must be very simple – what am I doing wrong?
a possible solution to this issue would be adding a method returning the element to index. For the profile.institution.name case:
# profile.rb
# ...
belongs_to :institution
# ...
define_index do
indexes institution_name
end
def institution_name
self.institution.name
end
# ...
Also the ", :as => ..." syntax is not supported on thinkingtank.
I would also recommend giving a try to Tanker: https://github.com/kidpollo/tanker
Regards.
Adrian

Creating url slugs for tags with acts_as_taggable_on

I would like to create url slugs for tags managed by the acts_as_taggable_on gem. For instance instead of urls like http://myapp.com/tags/5, I would like to have http://myapp.com/tags/my-tag (where 'my tag' is the tag's unique name).
In models that I create myself I usually do this by overriding the model's to_param method, and creating a "slug" field in the model to save the result of the new to_param method. I tried doing this with the Tag model of ActsAsTaggableOn, but it is not working.
I can otherwise override things in the tag.rb class of ActsAsTaggableOn as follows:
# Overwrite tag class
ActsAsTaggableOn::Tag.class_eval do
def name
n = read_attribute(:name).split
n.each {|word| word.capitalize!}.join(" ")
end
end
However, if I try to override the to_param method in that same block with a method definition like:
def to_param
name.parameterize
end
Rails still generates and responds to routes with integer IDs rather than the parameterized name. In fact in the console if I try something like
ActsAsTaggableOn::Tag.find(1).to_param
The integer ID is returned, rather than the result of the overridden to_param method.
I'd rather not fork the gem and customize it if there is any way I can do it with my own application code. Thanks.
I'm using the friendly_id ( https://github.com/norman/friendly_id ) gem to manage slugs. My method to create slugs for my tags is similar to yours, but a lit bit simpler.
I've just created the initializer act_as_taggable_on.rb with the following code:
# act_as_taggable_on.rb
ActsAsTaggableOn::Tag.class_eval do
has_friendly_id :name,
:use_slug => true,
:approximate_ascii => true,
:reserved_words => ['show', 'edit', 'create', 'update', 'destroy']
end
And then:
#user = User.new :name => "Jamie Forrest"
#user.tag_list = "This is awesome!, I'm a ruby programmer"
#user.save
And voilá:
ActsAsTaggableOn::Tag.find('this-is-awesome') #=> #<Tag id: 1, name: "This is awesome!">
ActsAsTaggableOn::Tag.find('im-a-ruby-programmer') #=> #<Tag id: 2, name: "I'm a ruby programmer">
Hope this help...
#vitork's code is a good start but doesn't work for newer versions of friendly_id and acts_as_taggable. Here's an updated initializer:
ActsAsTaggableOn::Tag.class_eval do
extend FriendlyId
friendly_id :name,
:use => :slugged,
:slug_column => :permalink,
:reserved_words => ['show', 'edit', 'create', 'update', 'destroy']
end
My db column is called permalink, you can use slugged if you prefer. Btw, I'm using the following:
friendly_id (4.0.5)
acts-as-taggable-on (2.2.2)
Thanks Vitork for the initial code!
To make this work with latest version (Rails 4.x, friendly_id 5.x) here are the steps you should follow:
Create migration to add slug to tags table
# rails generate migration add_slugs_to_tags
class AddSlugToTags < ActiveRecord::Migration
def change
add_column :tags, :slug, :string
add_index :tags, :slug
end
end
You can rename the :slug column - you should specify the column name in the initializer if you change it. Don't forget to run the migration rake db:migrate.
Create an initializer for ActsAsTaggableOn
# config/initializers/acts_as_taggable_on.rb
ActsAsTaggableOn::Tag.class_eval do
extend FriendlyId
friendly_id :name, use: :slugged
end
When searching for tags you have to use ActsAsTaggableOn::Tag.friendly.find 'tag-name' or add :finders to friendly_id :use call to use find directly on the model. Read more in friendly_id guides.
Actually the answer is much simplier and you dont need to use friendly_id or any other unnecessary extension.
сonfig/initializers/act_as_taggable_on.rb
ActsAsTaggableOn::Tag.class_eval do
before_save { |tag| tag.slug = name.parameterize if name_changed? }
def to_param
slug
end
end
Add a slug column if you need to, otherwise skip before_save callback.
Then in the view, instead of iterating like
article.tag_list.each do |tag|..
you'll iterate like this
article.tags.each
because tag_list gives you only strings, whereas with tags u have real tags instances.
And at least in the controller
if params[:tag]
tag = ActsAsTaggableOn::Tag.find_by_slug(params[:tag])
#articles = Article.moderated.includes(:user).tagged_with(tag)
end
There is another way.
Create a controller for the tags with single action:
rails g controller tags index
In routes.rb change the generated route to:
get 'tags/:tag' => 'tags#index', as: :tag
In tags_controller.rb add this code:
def index
#tag = params[:tag]
#entries = Entry.tagged_with(#tag)
end
where Entry is a model name.
Now you able to get all entries with nice urls like example.com/tags/animals
Usage in views:
- #entry.tags.each do |tag|
= link_to tag, tag_path(tag.name)

Resources