I'm searching a gem for Rails for alphabetical pagination. I wish I could have a list of first letters found in the result (I mean, if there is no row beginning with 'a', I don't want the 'a' to be display on the pagination links). Is this kind of gem already exists?
Thanks in advance!
This wouldn't be too hard to create at all, for example if you had a find, maybe like:
#all_words = Word.select("words.word")
…which returned a result a result set such as a list of words like this:
["alphabet", "boy", "day", "donkey", "ruby", "rails", "iPad"]
…the you could do this:
#all_words.collect {|word| word[0,1]}.uniq.sort
which would return:
["a", "b", "d", "r", "i"]
The .collect {|word| word[0,1]} stores the first letter of each word into a new array whilst uniq filters out the unique letters and sort sorts these alphabetically.
Simply assign this to a variable and you can use it in your view like so:
<ul>
<% #first_letters.each do |letter| %>
<%= content_tag :li, link_to(letter, words_pagination_url(letter), :title => "Pagination by letter: #{letter}") %>
<% end %>
</ul>
Your controller action could then decide what to do with the param from the pagination if one is passed in:
def index
if params[:letter]
#words = Word.by_letter(params[:letter])
else
#words = Word.all
end
end
And then the scope in your model would look something like:
scope :by_letter,
lambda { |letter| {
:conditions => ["words.word LIKE ?", "#{letter}%"]
}}
Your routes require something like:
match "words(/:letter)" => "words#index", :as => words_pagination
I haven't tested this all the way through but it should set you on the right path.
To get a dynamic select from the appropriate table, you can use a dynamic SQL finder.
In this example, we select from a table named 'albums', and fabricate a column 'name' to hold the values. These will be returned in the 'Album' model object. Change any of these names to suit your needs.
Album.find_by_sql("SELECT DISTINCT SUBSTR(name,1,1) AS 'name' FROM albums ORDER BY 1")
Note that you can't use the Album model objects for anything except querying the 'name' field. This is because we've given this object a lobotomy by only populating the 'name' field - there's not even a valid 'id' field associated!
I've created an alphabetical pagination gem here:
https://github.com/lingz/alphabetical_paginate
For anyone still having issues in this domain.
Related
I have a bit of code in my user.rb model like this:
def self.aggregate(articles)
array = []
articles.each do |a|
array << {
:id => a.nid,
:views => a.daily_view_metrics.sum_views(a.nid),
:date => a.daily_view_metrics.latest_date(a.nid),
:title => a.daily_view_metrics.latest_title(a.nid),
:visits => a.daily_view_metrics.sum_visits(a.nid)
}
end
return array
end
In my user_controller I pass into the show method #metrics = User.aggregate(#articles) (#articles being simply a subset of articles for that user)
Now in my view (user#show) i call #metrics.each do |m| and then output all the different things in a table. Now according to this video it seems that the link_to method with a url parameter seems to be the best way to have users dynamically switch what they want to sort against.
How can I input that url parameter to sort the array? I tried calling #metrics.sort_by{|h| h[params[:sort]]}.each do |m| with :sort being the url parameter from my links (i.e. the views table header link click passes :sort => ":views" in. Essentially I am trying to do this sort_by{|h| h[:views]} since that works fine for sorting the array. However nothing happens. The array isn't sorted.
EDIT:
I solved it by making the aggregate method pass the key in as a string (i.e. "id" as opposed to :id). then the url params works beautifully.
<%= link_to "Views", :sort => "views"%> now sorts it by views in ascending order.
To order in descending mode you can negate - the element that you are using to sort by.
Ordering by ascending and then do revert to your collection is inefficient.
For instance
$> [{a: 'a1', b: 1}, {a: 'a2', b: 2}].sort_by{ |h| -h[:b] }
# => [{:a=>"a2", :b=>2}, {:a=>"a1", :b=>1}]
$> [{a: 'a1', b: 1}, {a: 'a2', b: 2}].sort_by{ |h| h[:b] }
# => [{:a=>"a1", :b=>1}, {:a=>"a2", :b=>2}]
In the form of your view, you will have something like this (a RadioButton e.g but it could be a Select or whatever you prefer):
<%= radio_button_tag 'radio_order', 'ascending', true %> Ascending
<%= radio_button_tag 'radio_order', 'descending' %> Descending
<%= submit_tag "Order" %>
Then in your helper get the value using params[:radio_order]:
aggregate('views', params[:radio_order])
Here is my code:
<%= DimensionVersion.where(:dimension_id => 1).select("name") %>
I expect to get a list of dimension version names where :dimension_id => 1. There are four in the database.
Instead I get this:
#<ActiveRecord::Relation:0x3d351c8>
EDIT:
I figured out how to return what I wanted (sort of) with this:
<%= DimensionVersion.select("name").where(:dimension_id => 1).all %>
Which returns:
[#<DimensionVersion name: "Default">, #<DimensionVersion name: "Test1">, #<DimensionVersion name: "Test2">, #<DimensionVersion name: "Test3">]
However, I don't want it returned with #<DimensionVersion Name: ... >. I tried removing = from the leading tag, but then nothing returned.
DimensionVersion.where(:dimension_id => 1).select("name")
I think you need the pluck method.
Rewrite the above as:
DimensionVersion.where(:dimension_id => 1).pluck(:name)
Similarly even a higher level construct like collect can be used as:
DimensionVersion.where(:dimension_id => 1).collect(&:name)
Hope this helps.
AR returns Relation so that you can chain conditions etc. If you want the actual results, call #all, #first, #each,... on it:
DimensionVersion.where(:dimension_id => 1).select("name").all
Querying with rails is such a pain in the butt I'm about to abandon the whole framework and go back to php.
You might want to read the guides: Active Record Query Interface.
I was able to get rid of the column names by using the collect method like so:
DimensionVersion.select("name").where(:dimension_id => 1).all.collect { |d| [d.name]}
I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.
Rails 2.3.11
I did read this answer, but it's not working for me.
I would like the default option for this selection box to be the event_id passed through the URL.
f.select :event_id, #events, :selected => url_args["event_id"]
An example #events is[["SC 2 Tournament", 195], ["Obstacle Course", 196], ["Mortal Combat", 197]]
The following also didn't work:
adding ".to_i" to "url_args["event_id"
using options_for_select(#events, url_args["event_id"]
Thank you!
This is a lot easier if you use the collection_select helper:
f.collection_select :event_id, #events, :id, :name
Then to select the default option (and have that be selected on pageload), you can simply assign it to whatever Object it is that the form is for within the controller. eg like this:
def new
#events = Event.all
#thing = Thing.new(:event => #events.first)
end
I'm not sure where your url_args comes from, but I'm guessing it's probably from a param in the URL, in which case you can do this:
Thing.new(:event_id => params[:event_id])
One last thing - collection_select won't quite work with #events as you've specified it, as you're using a nested Array, when it's expecting an Array of Objects that it can call id and name on in order to retrieve the values and display text for the select options. To fix that, simply redefine #events within your controller, using one of the ActiveRecord finders, such as Event.all or Event.find(..).
Make sense?
I'm new to rails and need some help with iterating through a sql result.
I have a method in my model that uses find_by:
def self.find_country()
#countries = CountryTable.find_all_by_country_status('Y', :select => "country_name")
#countries.each do |c|
puts "#{c.inspect}"
end
end
This is what I have in the view:
<%= select_tag "country", options_for_select(CountryTable.find_country) %>
Then I get this awkward #<CountryTable:0x30509bc> instead of the country name shown for each select option in the source:
<option value="#<CountryTable:0x30509bc>">#<CountryTable:0x30509bc></option>
<option value="#<CountryTable:0x3050944>">#<CountryTable:0x3050944></option>
<option value="#<CountryTable:0x30508e0>">#<CountryTable:0x30508e0></option>
I'm so new to rails that I'm probably not even go about this right.
Even if you put a :select statement in the find statement it will still return objects of the model class (CountryTable). You need to extract the country_name attributes from the objects.
The best way to convert an array of objects by converting each object is to use map:
def self.find_country
find_all_by_country_status('Y', :select => "country_name").map {|c| c.country_name }
end
This passes each country that find_all_by_country_status returns to the block. The block in turn returns the country name of the country. Then map combines those results into a new array and the method returns that.
As a side note, the name of your model should probably be just "Country", not "CountryTable". In Rails, models are named after the objects they represent. So if your model is "Country", each object (each row in the model's database table) represents a country.