Undefined method sort_by in rails - ruby-on-rails

I am facing issues while using sort or sort_by in my presenter and controller. It says undefined method 'sort_by' .
Does it belong to any particular class? I have looked it up online but I am not able to find anything concrete.
Can any one shed light on this?
Here is my presenter code-
def sortDocument()
#list.sort do |a, b|
(b.published_date <=> a.published_date) ||
a.display_name <=> b.display_name
#list
end
and alternatively
def sortDocument()
#list.sort_by!{ |m| m.published_date }
end
EDIT:
Error message:
undefined method `sort_by!' for #<DocumentsBureauServices::DocumentList:0x007ff8250f8a28>
presenter-
class DocumentPresenter
def initialize(list)
#list = list
#list = sort_document()
end
def sortDocument()
#list.sort do |a, b|
(b.published_date <=> a.published_date) ||
a.display_name <=> b.display_name
#list
end
end
and alternatively
def sortDocument()
#list.sort_by!{ |m| m.published_date }
end

Sorts enum using a set of keys generated by mapping the values in enum through the given block...to use sort_by ...
%w{ apple pear fig }.sort_by {|word| word.length}
#=> ["fig", "pear", "apple"]

It looks like the DocumentsBureauServices::DocumentList doesn't include the enumerable module and doesn't have access to the sort_by method.
If indeed DocumentList is a decorator around an enumerable object then you could use a delegator. ActiveSupport has a method dedicated to delegating: http://apidock.com/rails/Module/delegate
In your case, you could use it like this:
class DocumentsBureauServices::DocumentList
delegate :sort_by!, to: :my_enumerable
def initialize(enumerable)
#my_enumerable = enumerable
end
end
And then you could call sort_by! on your list object like this:
#list = DocumentsBureauServices::DocumentList.new(Document.all)
#list.sort_by! {|m| m.published_date}
#=> [#Document1, #Document2, etc]
If you want to know more about creating your own presenters, might I suggest the excellent RailsCast about it http://railscasts.com/episodes/287-presenters-from-scratch
I personally use the Draper library for presenters: https://github.com/drapergem/draper which makes presenting ActiveRecord objects a breeze. There's a RailsCast about it here: http://railscasts.com/episodes/286-draper

Related

How to create an array of methods in rails

def id_attachment_require_upload?
!object.id_attachment?
end
...
def work_attachment_require_upload?
!object.work_attachment?
end
I want to make it like below.
array = %w(id address work)
array.each do |a|
def #{a}_attachment_require_upload?
!object.#{a}_attachment?
end
end
Is there any way for me to create a array of methods automatically in rails to save me from the redundant work.
array = %w(id address work)
array.each do |a|
define_method "#{a}_attachment_require_upload?" do
!object.public_send("#{a}_attachment?")
end
end
Arup's answer looks like it's the way to go but I'm not sure if object.#{a}_attachment? will work. If it does, then I learned something new today. You can also use public_send.
array = %w[id address work]
array.each do |a|
define_method "#{a}_attachment_require_upload?" do
!object.public_send("#{a}_attachment?")
end
end

Passing multiple scopes to Concern Method - Ruby on Rails

In the process of drying my Rails app code, I have created the following concern that is used to generate the contents of an index method.
define_method(:generate_index) do |string, scope|
instance_variable_set( "##{string}", string.camelize.constantize.public_send(scope))
end
I use this code to generate something like the following:
def index
generate_index("foo", "all")
# #foo = Foo.all
end
What I'd like to do is to have the define method accept a number of scopes. I tried passing in an array of scopes but that results in an error.
Any ideas?
Thanks
You could use the splash * operator:
define_method(:generate_index) do |klass, *scopes|
scope = klass.to_s.camelize.constantize
scopes.each { |s| scope = scope.send(s) }
instance_variable_set("##{string}", scope)
end
def index
generate_index(:foo, :all, :where_not_test)
# #foo = Foo.all.where_not_test
end

Rails - Pass collection to ActiveModel object

I am using rails to make a datatable that paginates with Ajax, and I am following railscast #340 to do so.
This episode makes use of a normal ActiveModel Class called ProductsDatatable or in my case OrdersDatatable to create and configure the table. My question has to do with ruby syntax in this class. I am trying to pass a collection of orders to the OrdersDatatable object, from the controller. I want to access this collection in the fetch_orders method.
I create the table object like this in order.rb:
#datatable = OrdersDatatable.new(view_context)
#datatable.shop_id = #current_shop.id
#datatable.orders_list = #orders # which is Order.in_process
And my OrdersDatatable class looks like this: (the important parts which probably need to change is the second line in initialize and the first line in fetch_orders)
class OrdersDatatable
include Rails.application.routes.url_helpers
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TagHelper
delegate :params, :h, :link_to, :number_to_currency, to: :#view
attr_accessor :shop_id, :orders_list
def initialize(view)
#view = view
#orders_list = self.orders_list
end
def current_shop
Shop.find(shop_id)
end
def as_json(options = {})
{
sEcho: params[:sEcho].to_i,
iTotalRecords: orders.count,
iTotalDisplayRecords: orders.count,
aaData: data
}
end
private
def data
orders.map do |order|
[
order.id,
order.name,
h(time_tag(order.date_placed.in_time_zone)),
order.state,
order.source,
order.payment_status,
h(order.delivered? ? 'shipped' : 'unshipped'),
h(number_to_currency order.final_total, unit: order.currency.symbol),
h(link_to 'details', edit_admin_shop_order_path(current_shop, order)),
h(link_to 'delete', admin_shop_order_path(current_shop, order), method: :delete, data: { confirm: 'Are you sure?' } ),
]
end
end
def orders
#orders ||= fetch_orders
end
def fetch_orders
orders = orders_list.order("#{sort_column} #{sort_direction}")
orders = orders.page(page).per_page(per_page)
if params[:sSearch].present?
orders = orders.where("title like :search", search: "%#{params[:sSearch]}%")
end
orders
end
def page
params[:iDisplayStart].to_i/per_page + 1
end
def per_page
params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
end
def sort_column
columns = %w[id name date_placed state source payment_status delivered final_total]
columns[params[:iSortCol_0].to_i]
end
def sort_direction
params[:sSortDir_0] == "desc" ? "desc" : "asc"
end
end
When I change the first line in fetch_orders to this
orders = Order.in_process.order("#{sort_column} #{sort_direction}")
which is the hard-coded equivalent, it does work. So I just need the correct syntax
Short answer: If you've got an array, and want to sort it, use the sort_by method:
orders = orders_list.sort_by{|order| "#{order.sort_column} #{order.sort_direction}"}
Long answer: The reason your original code doesn't work is that in this case
Order.in_process.order("#{sort_column} #{sort_direction}")
you are building a query. in_process is a named scope (passing in some conditions), and .order tells rails what to order the query by. Then, when it runs out of chained methods, the query executes (runs some sql) and gets the records out of the DB to build a collection of objects.
Once you are working with a collection of objects, you can't call the .order method on it, as that's just used to assemble an sql query. You need to use Array#sort_by instead. sort_by takes a code block, into which is passed each object in the collection (as order in my example but you could call it anything, it's just a variable name).
BTW, if you just want to call a method on all the objects to sort them, you can use a "shortcut syntax" like .sort_by(&:methodname). This uses a little trick of ruby called Symbol#to_proc (http://railscasts.com/episodes/6-shortcut-blocks-with-symbol-to-proc).
So, for example, if there was a method in Order like so
def sort_string
"#{self.sort_column} #{self.sort_direction}"
end
then you could change your code to
orders = orders_list.sort_by(&:sort_string)
which is neat.
If you have an array, then you can sort like this.
orders = orders_list.sort! {|a,b| a.sort_column <=> b.sort_direction}

Override ActiveRecord find

I have done this in Rails 2.3.10 and 3.0.3 and it works
def self.find(*args)
records = super
# Manipulate Records here.
end
I am looking for a base finder function in Rails 3 which I can replace for this functionality to be applied to Post.all, Post.first, Post.last etc.
My advice ... make a scope or a class method to do this instead:
e.g.
scope :my_scope, lambda {|...| ...}
then to apply
TheClass.my_scope.all
TheClass.my_scope.first
TheClass.my_scope.last
all, first, and last are all just wrappers for find so redefining find should affect all of those. Take a look at how they're implemented in ActiveRecord::FinderMethods.
I believe you are looking for this:
# File activerecord/lib/active_record/relation/finder_methods.rb, line 95
def find(*args)
return to_a.find { |*block_args| yield(*block_args) } if block_given?
options = args.extract_options!
if options.present?
apply_finder_options(options).find(*args)
else
case args.first
when :first, :last, :all
send(args.first)
else
find_with_ids(*args)
end
end
end
This will do what the original question was asking.
def self.find_by_sql(*args)
records = super
# Manipulate Records here
return records
end

Sort collection of object in rails?

I know I can use something like User.sort {|a, b| a.attribute <=> b.attribute} or User.find and order, but is it a way like comparable interface in Java, so every time I called sort on User object it will do sort on the predefined attributes.
Thanks,
You can do that by defining the <=> method for your objects. That way, you should be able to just say: collection.sort for non-destructive sort, or collection.sort! for in-place sorting:
So, example here:
class A
def <=>(other)
# put sorting logic here
end
end
And a more complete one:
class A
attr_accessor :val
def initialize
#val = 0
end
def <=>(other)
return #val <=> other.val
end
end
a = A.new
b = A.new
a.val = 5
b.val = 1
ar = [a,b]
ar.sort!
ar.each do |x|
puts x.val
end
This will output 1 and 5.

Resources