Rails3: How to pass param into custom will_paginate renderer? - ruby-on-rails-3

I've got a custom will_paginate renderer that overrides WillPaginate::ViewHelpers::LinkRenderer's link method like so:
def link(text, target, attributes = {})
"<a href='/users/95/friends_widget?page=#{target}' rel='next' data-remote='true'>#{text}</a>"
end
...and that works great, except you can see the hard-coded 95 in that link. How would I pass a parameter (e.g. user or user's ID) into the custom renderer via the Rails view?
<%= will_paginate(#user_friends, :remote => true, :renderer => FriendsRenderer) %>
Or is there something I'm missing, some easier way to do it?
BTW: #user_friends isn't available in the custom renderer, and I've already tried just adding params onto the end of that will_paginate call, e.g. :user => user)

will_paginate lets you pass in :params for the links:
will_paginate(#user_friends, :params => { :user_id => 95 })

View:
<%= will_paginate #user_friends, :renderer => 'FriendsRenderer',
:remote => true,
:link_path => friends_widget_user_path(#user) %>
class FriendsRenderer < WillPaginate::LinkRenderer
def prepare(collection, options, template)
#link_path = options.delete(:link_path)
super
end
protected
def link(page, text, attributes = {})
# Here you can use #link_path
end
end
Note that this works for the will-paginate version: 2.3.6

Related

Draper: How to convert all allowed attributes and public methods of the decorated model into json

I'm looking for an easy way to convert decorated model into json to use in my client-side templates. I'd like to find a solution where all allowed attributes and public methods of the decorated model will persist in json.
Currently I have backbone RIA with rails backend. I'm using haml_coffee_assets gem for client-side templating. Draper is there to provide decoration for my User model
class ContactDecorator < Draper::Base
include Draper::LazyHelpers
decorates :user
allows :login, :id, :total_unread, :total_messages
def for_json
{
:new_messages => new_messages,
:avatar_link => avatar_link,
:login_name_link => login_name_link,
:id => model.id,
:name => model.login,
:avatar => model.avatar.url(:small),
:humanized_messages_number =>
}
end
def humanized_messages_number
pluralize(user['total_messages'], t("share_my_trip.messages.Message"), t("share_my_trip.messages.Messages")) + " #{new_messages}"
end
def new_messages
model['total_unread'].to_i > 0 ? "(#{model['total_unread'].to_i} #{t("share_my_trip.messages.new")})" : ''
end
def avatar_link
link_to(image_tag(model.avatar.url(:small), :size => "32x32", :onerror => "this.src='/avatars/original/missing.png'"), share_my_trip_user_path(:id => model.login), :id => "user-nick-#{model.id}", :class => "author")
end
def login_name_link
link_to(model.login, share_my_trip_user_path(:id => model.login), :id => "user-nick-#{model.id}", :class => "author")
end
end
my controller code looks like:
def index
#dialogs = ContactDecorator.decorate(current_user.contacts).collect{|c| c.for_json}
end
and than in my view I'm just initializing backbone app:
:javascript
App.init({dialogs: #{#dialogs.to_json}})
Is there a way to delete ugly #for_json decorator method?

How to treat additional parameters on Rails model creation?

I have a register form for users to sign up to my rails app and I added a checkbox for users who do not want a special service. Here is the code from the view :
<%= form_for(#user, :url => { :action => "create" }, :html => { :multipart => true }) do |f| %>
<%= f.check_box :wantsaoc, :onchange => "check_field(this)" %>
[etc...]
<% end %>
In my model i have a wantsaoc method thats returns me a boolean depanding on some other attributes.
How can i handle the creation so that i can catch the wantsaoc parameter and behave depending on it ?
Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic before or after an alteration of the object state. For example,
class User < AR::Base
..
before_save :do_something
def do_something
if wantsoac.eql?('true')
.. # Do something
end
end
end
Go through various callbacks you can use and choose when and what you need to do.

Using jquery tokeninput and acts_as_taggable_on

I've implemented the framework outlined in this post: How to use jquery-Tokeninput and Acts-as-taggable-on with some difficulty. This is working insofar as prepopulating with the appropriate theme and ajax search, but when I enter a new tag, it is immediately deleted when the text area loses focus. I'm not sure what I'm doing wrong. Here's some of my relevant code:
User Model (does the tagging):
class User < ActiveRecord::Base
[...]
# tagging
acts_as_tagger
Item Model (accepts a tag):
class Item < ActiveRecord::Base
attr_accessible :title, :tag_list
#tagging functionality
acts_as_taggable_on :tags
Item Controller:
def tags
#tags = ActsAsTaggableOn::Tag.where("tags.name LIKE ?", "%#{params[:q]}%")
respond_to do |format|
format.json { render :json => #tags.collect{|t| {:id => t.name, :name => t.name }}}
end
end
On my form partial:
<%= f.input :tag_list, :label => "Tags", :input_html => { :class => "text_field short", "data-pre" => #item.tags.map(&:attributes).to_json }, :hint => "separate tags by a space" %>
my routes:
get "items/tags" => "items#tags", :as => :tags
resources :items
[almost there!!!]
the js on the form [note: the id of the element is assigned dynamically]:
<script type="text/javascript">
$(function() {
$("#item_tag_list").tokenInput("/art_items/tags", {
prePopulate: $("#item_tag_list").data("pre"),
preventDuplicates: true,
crossDomain: false,
theme: "facebook"
});
});
</script>
If you still want to use Jquery TokenInput and add tags there are different ways to do it.
1.
This is actually from my same question; the newest answer: How to use jquery-Tokeninput and Acts-as-taggable-on
This could go in your controller.
def tags
query = params[:q]
if query[-1,1] == " "
query = query.gsub(" ", "")
Tag.find_or_create_by_name(query)
end
#Do the search in memory for better performance
#tags = ActsAsTaggableOn::Tag.all
#tags = #tags.select { |v| v.name =~ /#{query}/i }
respond_to do |format|
format.json{ render :json => #tags.map(&:attributes) }
end
end
This will create the tag, whenever the space bar is hit.
You could then add this search setting in the jquery script:
noResultsText: 'No result, hit space to create a new tag',
It's a little dirty but it works for me.
2.
Check out this guy's method: https://github.com/vdepizzol/jquery-tokeninput
He made a custom entry ability:
$(function() {
$("#book_author_tokens").tokenInput("/authors.json", {
crossDomain: false,
prePopulate: $("#book_author_tokens").data("pre"),
theme: "facebook",
allowCustomEntry: true
});
});
3.
Not to sure about this one but it may help: Rails : Using jquery tokeninput (railscast #258) to create new entries
4.
This one seems legit as well: https://github.com/loopj/jquery-tokeninput/pull/219
I personally like the first one, seems easiest to get and install.

Is view_context no longer available in Rails 3 views?

in a current Rails 3.0.9 app of mine I had a few .js.erb templates that were using view_context in them so I could call fields_for on it during a ajax request. This was letting me build some nested attribute form fields via ajax. But upon upgrading to Rails 3.1 I'm getting the follow error:
ActionView::Template::Error (undefined local variable or method `view_context' for #<#:0x1057b9f70>):
Was this removed/deprecated recently? Is there another way I can build nested fields_for inputs without having the parent FormBuilder handy? It seems view_context is still available in the controller, but I was hoping to keep this markup generation in the View layer.
My .js.erb template looked like this
<% meal_item_fields = view_context.fields_for :meal_items, Meal.new.meal_items.new, :child_index => "new_meal_items" do |f|
render :partial => 'meal_items/meal_item_fields', :locals => {:meal_item_form => f}
end
%>
$("#meal-items").append("<%= escape_javascript(meal_item_fields) %>");
According to api docs it is deprecated in >= 3. Source of 3.0.9 returned self for view_context. I think if you were to try without view_context it would just work.
<% meal_item_fields = fields_for :meal_items, Meal.new.meal_items.new, :child_index => "new_meal_items" do |f|
render :partial => 'meal_items/meal_item_fields', :locals => {:meal_item_form => f}
end %>
$("#meal-items").append("<%= escape_javascript(meal_item_fields) %>");
You might want to add helper_method :view_context in your controller.

Routing with id and handle in Rails

I'm trying to set up rails to use both the ID and the Handle (which is just an URL safe version of the title) of a blog post in the route.
match '/articles/:id/:handle', :to => 'articles#show'
resources :articles
This works, of course -- but I can't seem to set up the to_param method in the model os the longer URL -- with the handle attached, is the default.
This doesn't work (not that I really expected it to):
def to_param
"#{id}/#{handle}"
end
I get a No route matches {:action=>"edit", :controller=>"articles", error. I also tried just using the handle, but then Rails generates links to the resource just using the handle and not the ID. I know I can do it with a - in stead of a /, but I prefer the /. Any way to make this work? If I have to add some extra paremeters to my link_to helpers, that's okay.
Did you try to pass a Hash to link_to?
link_to "Link", {:id => #article.id, :handle => #article.handle}
Update
You have to modify your routes:
match '/articles/:id/:handle', :to => 'articles#show', :as => :article_with_handle
and use the following helper to generate the link:
link_to "Link", article_with_handle_path(:id => #article.id, :handle => #article.handle)
You can override the helper to simplify things:
def article_with_handle_path(article)
super(:id => article.id, :handle => article.handle)
end
and use it like this:
link_to "Link", article_with_handle_path(#article)
Okay, here's what I did to remove the query string problem from the answer above:
Changed the route to this:
match '/articles/:id/:handle' => 'articles#show', :as => :handle
Removed the to_param method from the model and then generated the link like this:
link_to 'Show', handle_path(:handle => article.handle, :id => article.id) %>
That works, but could be condensed, obviously, with the helper above. Just change the one line to: args[1] = handle_path(:id => args[1].id, :handle => args[1].handle)