Does anyone know of a way to get collection_select to name its fields for the text methods' names and not their values?
I've got print_100, print_200, and print_500 and a plan to add more when necessary. I'd like the values of the select box to read from Billing all the fields that start with print_ so the select box would just have options like 100, 200, and 500.
f.collection_select(:print_quantity, Billing.all, :print_100, :print_100)
Any thoughts? Cheers.
I'm not as familiar with this part of rails as I'd like to be, so be gentle.
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select
the syntax is
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
If you were to change the second parameter (method) to an actual method (rather than just the attribute that you want from the billing object) you can make the value whatever you would like.
If that doesn't work (or if you're not allowed to substitute the attribute for a method) then you may be able to make it work using the 5th or 6th parameters, value_method and text_method, which define what values should be applied to the tags.
Anyway, this answer is mostly to point you in (hopefully) the right direction, since I'm not certain of the method or how it works.
Good luck.
Thanks to #DavidDraughnn for the idea for this solution. I wrote a method in the relevant helper, thus:
def get_quantities
#quantities = {}
Billing.column_names.each do |a|
if a.match(/^print_/)
#quantities[a.delete "print_"] = a.delete "print_"
end
end
return #quantities
end
And I've adjusted collection_select to select, thus:
<% get_quantities %>
<%= f.select(:print_quantity, #quantities, {:prompt => "Please select..."}) %>
Hope that helps someone.
Related
I have a table KmRelationship which associates Keywords and Movies
In keyword index I would like to list all keywords that appear most frequently in the KmRelationships table and only take(20)
.order doesn't seem to work no matter how I use it and where I put it and same for sort_by
It sounds relatively straight forward but i just can't seem to get it to work
Any ideas?
Assuming your KmRelationship table has keyword_id:
top_keywords = KmRelationship.select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20)
This may not look right in your console output, but that's because rails doesn't build out an object attribute for the calculated frequency column.
You can see the results like this:
top_keywords.each {|k| puts "#{k.keyword_id} : #{k.freqency}" }
To put this to good use, you can then map out your actual Keyword objects:
class Keyword < ActiveRecord::Base
# other stuff
def self.most_popular
KmRelationship.
select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20).
map(&:keyword)
end
end
And call with:
Keyword.most_popular
#posts = Post.select([:id, :title]).order("created_at desc").limit(6)
I have this listed in my controller index method which allows the the order to show the last post with a limit of 6. It might be something similar to what you are trying to do. This code actually reflects a most recent post on my home page.
I want to create pagination for a messaging system in which the first page shown contains the oldest messages, with subsequent pages showing newer messages.
For example, if normal pagination for {a,b,c,d,e,f,g,h,i} with 3 per page is:
{a,b,c}, {d,e,f}, {g,h,i}
Then reverse pagination would be:
{g,h,i}, {d,e,f}, {a,b,c}
I plan to prepend the pages so the result is the same as normal pagination, only starting from the last page.
Is this possible with kaminari?
Kaminary.paginate_array does not produce query with offset and limit. For optimization reason, you shouldn't use this.
Instead you can do this:
#messages = query_for_message.order('created_at DESC').page(params[:page]).per(3)
Where query_for_message stands for any query which you use to retrieve the records for pagination. For example, it can be all the messages of a particular conversation.
Now in the view file, you just need to display #messages in the reverse order. For example:
<%= render :collection => #messages.reverse, :partial => 'message' %>
<%= paginate #messages %>
There's a good example repo on Github called reverse_kaminari on github. It suggests an implementation along these lines (Source).
class CitiesController < ApplicationController
def index
#cities = prepare_cities City.order('created_at DESC')
end
private
def prepare_cities(scope)
#per_page = City.default_per_page
total_count = scope.count
rest_count = total_count > #per_page ? (total_count % #per_page) : 0
#num_pages = total_count > #per_page ? (total_count / #per_page) : 1
if params[:page]
offset = params[:page].sub(/-.*/, '').to_i
current_page = #num_pages - (offset - 1) / #per_page
scope.page(current_page).per(#per_page).padding(rest_count)
else
scope.page(1).per(#per_page + rest_count)
end
end
end
All credits go to Andrew Djoga. He also hosted the app as a working demo.
One way to solve this problem would be this one:
Reverse pagination with kaminari?
It does not look very clean nor optimal, but it works :)
Yes, but the method I have come up with isn't exactly pretty. Effectively, you have to set your own order:
Message.page(1).per(3).order("created_at DESC").reverse!
The problem with this approach is twofold:
First the reverse! call resolves the scope to an array and does the query, nerfing some of the awesome aspects of kaminari using AR scopes.
Second, as with any reverse pagination your offset is going to move, meaning that between two repeat calls, you could have exactly 3 new messages send and you would get the exact same data back. This problem is inherent with reverse pagination.
An alternative approach would be to interrogate the "last" page number and increment your page number down towards 1.
I have the following validation in my ActiveRecord.
validates :active, :inclusion => {:in => ['Y', 'N']}
I am using the following to test my model validations.
should_not allow_value('A').for(:active)
should allow_value('Y').for(:active)
should allow_value('N').for(:active)
Is there a cleaner and more through way of testing this? I am currently using RSpec2 and shoulda matchers.
EDIT
After some looking around I only found, this probably an 'ok' way of testing this, shoulda does not provide anything for this and anyone who requires it can write their own custom matcher for it.(And probably contribute it back to the project). Some links to discussions that might be intresting:
Links which indicate to the above . Link 1 , Link 2
should_ensure_value_in_range This one comes close to what can be used, but only accepts ranges and not a list of values. Custom matcher can be based on this.
Use shoulda_matchers
In recent versions of shoulda-matchers (at least as of v2.7.0), you can do:
expect(subject).to validate_inclusion_of(:active).in_array(%w[Y N])
This tests that the array of acceptable values in the validation exactly matches this spec.
In earlier versions, >= v1.4 , shoulda_matchers supports this syntax:
it {should ensure_inclusion_of(:active).in_array(%w[Y N]) }
If you have more elements to test than a boolean Y/N then you could also try.
it "should allow valid values" do
%w(item1 item2 item3 item4).each do |v|
should allow_value(v).for(:field)
end
end
it { should_not allow_value("other").for(:role) }
You can also replace the %w() with a constant you have defined in your model so that it tests that only the constant values are allowed.
CONSTANT = %w[item1 item2 item3 item4]
validates :field, :inclusion => CONSTANT
Then the test:
it "should allow valid values" do
Model::CONSTANT.each do |v|
should allow_value(v).for(:field)
end
end
I found one custom shoulda matcher (in one of the projects I was working on) which attempts to coming close to test something like this:
Examples:
it { should validate_inclusion_check_constraint_on :status, :allowed_values => %w(Open Resolved Closed) }
it { should validate_inclusion_check_constraint_on :age, :allowed_values => 0..100 }
The matcher tries to ensure that there is a DB constraint which blows up when it tries to save it.I will attempt to give the essence of the idea. The matches? implementation does something like:
begin
#allowed_values.each do |value|
#subject.send("#{#attribute}=", value)
#subject.save(:validate => false)
end
rescue ::ActiveRecord::StatementInvalid => e
# Returns false if the exception message contains a string matching the error throw by SQL db
end
I guess if we slightly change the above to say #subject.save and let Rails validation blow up, we can return false when the exception string contains something which close matches the real exception error message.
I know this is far from perfect to contributed back to the project, but I guess might not be a bad idea to add into your project as a custom matcher if you really want to test a lot of the :inclusion validation.
this is my test (with shoulda helpers):
context "searching from header" do
setup do
Factory(:city, :name => 'Testing It')
ThinkingSphinx::Test.index 'city_core', 'city_delta'
ThinkingSphinx::Test.start
get :index,
:query => 'Testing It'
end
should respond_with(:success)
should assign_to(:results)
should "have one city on the result" do
assert_equal( assigns(:results).count, 1 )
assert_kind_of( assigns(:results).first, City )
end
ThinkingSphinx::Test.stop
end
Everything works fine except the test always say the count of the results is 0, not 1.
I have debugged this code and when the request reaches the controller, the Sphinx indexes are completely empty, even with the explicit call of index for it.
Am I doing something wrong here?
Any help appreciated.
I found out the problem... even tho the insertion in the database is right before the ThinkingSphinx.index, with transactional fixtures, after the setup block the records get deleted.
The solution was adding to the test the following line:
self.use_transactional_fixtures = false
Hope this helps anyone with the same problem.
Hi
I wonder how to work around the problem I have with the pagination gem "Kaminari".
For what I've understood you cant paginate #user = User.all.page(5)?
But what if I have this code and want to paginate that, is it possible or do I need to change the code?
#price = Price.joins(:retailer, :retailer => :profile).
where(['product_id=? AND size_id=?', params[:prod_id], params[:si_id]]).
group(:retailer_id).order("SUM((prices.price * #{params[:amount].to_i}) + profiles.shippingCost)").all
The only thing I receive right now when applying.page(5) to that code is
undefined method `page' for #<Class:0x000001023c4558>
You don't need the .all because the joins call, along with where and group, is returning an array of objects for you that meet your criteria. Remove your .all and call page on the instance variable (which you might want to rename to #pages or something else plural).