Override ActiveRecord find - ruby-on-rails

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

Related

Rails 5 - iterate until field matches regex

In my app that I am building to learn Rails and Ruby, I have below iteration/loop which is not functioning as it should.
What am I trying to achieve?
I am trying to find the business partner (within only the active once (uses a scope)) where the value of the field business_partner.bank_account is contained in the field self_extracted_data and then set the business partner found as self.sender (self here is a Document).
So once a match is found, I want to end the loop. A case exists where no match is found and sender = nil so a user needs to set it manually.
What happens now, is that on which ever record of the object I save (it is called as a callback before_save), it uses the last identified business partner as sender and the method does not execute again.
Current code:
def set_sender
BusinessPartner.active.where.not(id: self.receiver_id).each do |business_partner|
bp_bank_account = business_partner.bank_account.gsub(/\s+/, '')
rgx = /(?<!\w)(#{Regexp.escape(bp_bank_account)})?(?!\‌​w)/
if self.extracted_data.gsub(/\s+/, '') =~ rgx
self.sender = business_partner
else
self.sender = nil
end
end
end
Thanks for helping me understand how to do this kind of case.
p.s. have the pickaxe book here yet this is so much that some help / guidance would be great. The regex works.
Using feedback from #moveson, this code works:
def match_with_extracted_data?(rgx_to_match)
extracted_data.gsub(/\s+/, '') =~ rgx_to_match
end
def set_sender
self.sender_id = matching_business_partner.try(:id) #unless self.sender.id.present? # Returns nil if no matching_business_partner exists
end
def matching_business_partner
BusinessPartner.active.excluding_receiver(receiver_id).find { |business_partner| sender_matches?(business_partner) }
end
def sender_matches?(business_partner)
rgx_registrations = /(#{Regexp.escape(business_partner.bank_account.gsub(/\s+/, ''))})|(#{Regexp.escape(business_partner.registration.gsub(/\s+/, ''))})|(#{Regexp.escape(business_partner.vat_id.gsub(/\s+/, ''))})/
match_with_extracted_data?(rgx_registrations)
end
In Ruby you generally want to avoid loops and #each and long, procedural methods in favor of Enumerable iterators like #map, #find, and #select, and short, descriptive methods that each do a single job. Without knowing more about your project I can't be sure exactly what will work, but I think you want something like this:
# /models/document.rb
class Document < ActiveRecord::Base
def set_sender
self.sender = matching_business_partner.try(:id) || BusinessPartner.active.default.id
end
def matching_business_partners
other_business_partners.select { |business_partner| account_matches?(business_partner) }
end
def matching_business_partner
matching_business_partners.first
end
def other_business_partners
BusinessPartner.excluding_receiver_id(receiver_id)
end
def account_matches?(business_partner)
rgx = /(?<!\w)(#{Regexp.escape(business_partner.stripped_bank_account)})?(?!\‌​w)/
data_matches_bank_account?(rgx)
end
def data_matches_bank_account?(rgx)
extracted_data.gsub(/\s+/, '') =~ rgx
end
end
# /models/business_partner.rb
class BusinessPartner < ActiveRecord::Base
scope :excluding_receiver_id, -> (receiver_id) { where.not(id: receiver_id) }
def stripped_bank_account
bank_account.gsub(/\s+/, '')
end
end
Note that I am assigning an integer id, rather than an ActiveRecord object, to self.sender. I think that's what you want.
I didn't try to mess with the database relations here, but it does seem like Document could include a belongs_to :business_partner, which would give you the benefit of Rails methods to help you find one from the other.
EDIT: Added Document#matching_business_partners method and changed Document#set_sender method to return nil if no matching_business_partner exists.
EDIT: Added BusinessPartner.active.default.id as the return value if no matching_business_partner exists.

Can this ruby method be refactored?

A user has multiple libraries, and each library has multiple books. I want to know if a user has a book in one of his libraries. I'm calling this method with: current_user.has_book?(book):
def has_book?(book)
retval = false
libraries.each do |l|
retval = true if l.books.include?(book)
end
return retval
end
Can my method be refactored?
def has_book?(book)
libraries.any?{|lib| lib.books.include?book}
end
Pretty much the simplest i can imagine.
Not Nil safe though.
def has_book?(book)
libraries.map { |l| l.books.include?(book) }.any?
end
map turns collection of library objects into collection of bools, depending do they include the book, and any? returns true if any of the elements in the array is true.
this has fewer lines, but your original solution could be more efficient if you return true as soon as you find a library that contains the book.
def has_book?(book)
libraries.each do |l|
return true if l.books.include?(book)
end
return false
end
btw. both php and ruby appeared about the same time.
This looks to be the most minified version, I can't see any way to minify it more:
def has_book?(a)c=false;libraries.each{|b|c=true if b.books.include?a};c end
I don't know why #Зелёный removed his answer but this was a good one IMO so I paste it here:
def has_book?(book)
libraries.includes(:books)
.where(books: {id: book.id})
.any?
end

Undefined method sort_by in 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

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

Ruby on Rails: Execute Logic Based on Selected Menu

I have a class that I use to contain select menu options for property types. It works fine. However, I need to be able to verify the selection and perform specific logic based on the selected option. This needs to happen in my Ruby code and in JavaScript.
Here is the class in question:
class PropertyTypes
def self.[](id)
##types[id]
end
def self.options_for_select
##for_select
end
private
##types = {
1 => "Residential",
2 => "Commercial",
3 => "Land",
4 => "Multi-Family",
5 => "Retail",
6 => "Shopping Center",
7 => "Industrial",
8 => "Self Storage",
9 => "Office",
10 => "Hospitality"
}
##for_select = ##types.each_pair.map{|id, display_name| [display_name, id]}
end
What is the best way to verify the selection? I need to perform specific logic and display user interface elements based on each type of property type.
Since I am storing the id, I would be verifying that the id is a particular property type. Something like:
PropertyTypes.isResidential?(id)
Then this method would look like this:
def self.isResidential?(id)
##types[id] == "Residential"
end
But now I am duplicating the string "Residential".
For JavaScript, I assume I would make an ajax call back to the model to keep the verification code DRY, but this seems like over kill.
Do I need to manually create a verification method for each property type or can I use define_method?
This seems so basic yet I am confused and burned out on this problem.
Thanks
===
Here's my solution:
class << self
##types.values.each do |v|
# need to remove any spaces or hashes from the found property type
v = v.downcase().gsub(/\W+/, '')
define_method "is_#{v}?", do |i|
type_name = ##types[i]
return false if type_name == nil #in case a bogus index is passed in
type_name = type_name.downcase().gsub(/\W+/, '')
type_name == v
end
end
end
It sounds like you can benefit from some Ruby meta-programming. Try googling "ruby method_missing". You can probably do something quick & dirty along the lines of:
class PropertyTypes
def method_missing(meth, *args, &block)
if meth.to_s =~ /^is_(.+)\?$/
##types[args.first] == $1
else
super
end
end
end
On the ruby side you could also use something like this to define dynamically these methods:
class << self
##types.values.each do |v|
define_method "is_#{v}?", do |i|
##types[i] == v
end
end
end

Resources