How to slugify a parent model with fields from a child model? - ruby-on-rails-3

I'd like to slugify the urls for a model in Rails 3, using Mongoid. The problem is the fields I want to use in the slug are located in a child model. I am using mongoid-slug gem to find a solution to this and my attempt so far is this:
class Building
references_one :address
def to_param
address.to_param
end
end
class Address
referenced_in :building
field :houseno
field :street
slug :houseno, :street
end
While this allows me to form the correct url by calling building_path(building), the page does not contain the correct values. Error message complains that object id is incorrect, and I'm not sure how to get Rails to listen and find the record by to_param.

For the curious, here is how I solved my own problem. I realized that I needed to use change the show action from
#building = Building.find(params[:id])
to
#building = Address.find_by_slug(params[:id]).building
And voila! It works.

Related

Add after_save callback for tag gem model to update tire index

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.

Cannot find FakeInput

I am trying add a fake input to simple_form using the following suggestion (rails simple_form fields not related to the model), but it does not work for me.
This is my HAML code for adding a fake input field.
= f.input :agrees_to_terms, :as => :fake
I put the following class in app/inputs/fake_input.rb
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input
template.text_field_tag(attribute_name, nil, input_html_options)
end
end
I get the following runtime error
No input found for fake
I have two initializers that were generated by Rails Composer: simple_form.rb and simple_form_bootstrap.rb
How can I make Rails find my FakeInput class?
I had the same issue, and a server restart resolved it.

Get current user in a model in Rails3

I am using ActiveAdmin. I need to store the User that created or updated every record.
So I added user_created_id and user_updated_id to every model.
On before_save I want to store the information about current_admin_user. The problem is that I cannot access current_admin_user in a model.
Is there a way to do that without breaking MVC?
I found this: http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
But I am not sure if it is safe.
Any help?
Do it in controller (it's the reason that controller exist) before save:
def create
#my_model = MyModel.create params[:my_model]
#my_model.user_created = current_user
#my_model.save
end
The same with the update method.

Virtual attribute not moved to the model hash inside params

I'm having a problem in my Rails 3.2 app where a virtual attribute sent restfully via JSON is not in the right place in the params hash. Well, it isn't where I expect. It remains to be seen if my expectations are correct. :)
I have a model using the standard virtual attribute pattern, like this:
class Track < ActiveRecord::Base
def rating
# get logic removed for brevity
end
def rating=(value)
# set logic
end
def as_json(options={}) # so my method is in the JSON when I use respond_with/to_json
super(options.merge(methods: [:rating]))
end
end
The JSON sent to my controller looks like this:
{"id":1,"name":"Icarus - Main Theme 2","rating":2}
To be clear, name and id are not virtual, rating is.
I end up with this in the params hash, after rails does its magic:
{"id"=>"1", "name"=>"Icarus - Main Theme 2", "rating"=>2, "track"=>{"id"=>"1", "name"=>"Icarus - Main Theme 2"}}
As you can see, id and name make it to the nested :track hash, but rating does not. Is this expected behavior? It breaks the (somewhat) standard practice of using the nested hash in the controller because the nested hash does not contain all the parameters I need.
Track.update(params[:id], params[:track]) # :track is missing rating
Thanks for your help!
I recently ran into this gotcha as well. The problem is, the params wrapper is looking at your model Track.attribute_names to determine how to map the data into a :track => {params} hash. If you don't have a model associated, the default will be to wrap the params based on the controller name, and include all of the values:
class SinglesController < ApplicationController
def create
#params[:single] will contain all of your attributes as it doesn't
# have an activerecord model to look at.
#track_single = Track.new(params[:single])
end
end
You can call wrap_parameters in your controller to tell action controller what attributes to include when its wrapping your params, like so:
class TracksController < ApplicationController
wrap_parameters :track, :include => :rating
#other controller stuff below
end
See more here: http://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html
Maybe if you assign the rating virtual attribute inside the nested hash like this:
def as_json(options={})
super(options.merge(:track => {:methods => #rating}))
end
It would behave the way you expected.
Just ran across this problem and figured out a pretty decent solution. Add the following to your ApplicationController
wrap_parameters exclude: [:controller, :action, :format] + ActionController::ParamsWrapper::EXCLUDE_PARAMETERS
This way, everything is nested under your resource (except for stuff Rails adds to the params hash) and you won't ever have to append to a controller specific call of wrap_parameters again. :D

How to user defined friendly URLs in Rails 3?

Now i have something like this
http://myapp.com/pages/1
http://myapp.com/pages/2
http://myapp.com/pages/3
http://myapp.com/pages/4
And each page belong to one user
What i need is to each user to set it's own custom name for the page.
I was thinking of using the friendly_id gem http://norman.github.com/friendly_id/
but I don't find any method to directly edit the slug to set a custom friendly url
how should i proceed?
FriendlyID is a great gem.
It shouldn't be hard to implement user defined page URL.
Create table pages with user_id and link
class User < ActiveRecord::Base
has_many :pages
class Page < ActiveRecord::Base
belongs_to :user
has_friendly_id :link # link is name of the column whose value will be replaced by slugged value
On the page#new you add an input for the link attribute.
Alternatively, you could set friendly_id on title or something else with :use_slug => true option. This way FriendlyID will take the title and modify it so it doesn't have and restricted characters. It will use it's own table to store slugs. Use cached_slug to increase performanse.
Updated
To give users a choice whether they wan't to set a custom link, you could do this:
Set friendly_id on the link field without slugs..
Make a virtual attribute permalink so you could show it in your forms.
In the before_filter, check whether the permalink is set.
If it is, write it to the link field.
If it's not, write title to the link field.
FriendlyID uses babosa gem to generate slugs. If you decide to use it as well, this is how your filter could look like:
protected
def generate_link
#you might need to use .nil? instead
self.link = self.permalink.empty? ? make_slug(self.title) : make_slug(self.permalink)
end
def make_slug(value)
value.to_slug.normalize.to_s #you could as well use ph6py's way
end
Adding to_param method to one of the models should help:
def to_param
"#{id}-#{call_to_method_that_returns_custom_name.parameterize}"
end
Hope this is what you are looking for :)
I am not using the friendly_url gem and am not sure whether my way is efficient. But it works fine for me.
I have a model called Node with id and friendly url field called url_title.
My routes.rb file:
resource 'nodes/:url_title', :to => 'Nodes#view'
nodes_controller.rb
class NodesController <ActiveController
def view
#node = Node.find_by_url_title(:params(url_title))
end
end
And use the #node variable to populate your view.
Now, whenever I type www.example.com/nodes/awesome-title , it takes me to the proper page. One argument against this can be need to create an index on a non-primary field. But I think that might be required for better performance even in the friendly_url gem. Also, the non-primary field url_title needs to be unique. Again, this might be required even for correct working for friendly_url .
Feel free to correct me if I am wrong in these assumptions.
There are a variety of ways, you can achieve this-
1) using Stringex
2) sluggable-finder
3) friendly_id
A complete step by step methodology with reasons for each to be used can be found out here. Happy reading!