Use the instance method in group_by - ruby-on-rails

I don't really like how it looks method all especially that part
.group_by{|x| x.week_number}.values as this part can be replaced?
week_number is the instance method in UserUpdate model.
user_updates_controller.rb
def all
#weekly_updates = current_user.user_updates.group_by{|x| x.week_number}.values
end
user_update.rb
def week_number
(((created_at.utc) - user.first_program_started_at.utc.beginning_of_day) / 86400 / 7).ceil.to_i || 1
end

Alright, I believe I understand better now, but let me know if this is still not quite what you want.
I believe in your case, you can simply do this:
#weekly_updates = current_user.user_updates.group_by(&:week_number).values
The &:week_number notation is a shorthand for creating a proc, and will effectively invoke the week_number method on the object passed in (in this case a UserUpdate object). Ultimately, you should see exactly the same result. There wouldn't be any performance difference, so its mainly just pretty-fying your code.
Check out this question and the answers for lots of examples and explanations on that &: notation.

Related

Why isn't the args parameter used in ActionController::Instrumentation::render?

I am new to Ruby and to Rails, and am trying to understand fully what I'm reading.
I am looking at some of the Rails source code, in this case action_controller/metal/instrumentation.rb.
def render(*args)
render_output = nil
self.view_runtime = cleanup_view_runtime do
Benchmark.ms { render_output = super }
end
render_output
end
I understand that *args is using the splat operator to collect the arguments together into an array. But after that, it stops making much sense to me.
I can't fathom why render_output is set to nil before being reassigned to equal super and then called with no arguments. I gather that some speedtest is being done, but coming from other languages I'd expect this to just be something more like Benchmark.ms(render_output) or perhaps Benchmark.start followed by render_output followed by Benchmark.end. I'm having a hard time following the way it works here.
But more importantly, I don't really follow why args isn't used again. Why bother defining a param that isn't used? And I mean, clearly it is getting used-- I just don't see how. There's some hidden mechanism here that I haven't learned about yet.
In this context, it is important to note how super works, because in some cases it passes implicitly arguments and you might not expect that.
When you have method like
def method(argument)
super
end
then super is calling the overridden implementation of method implicitly with the exact same arguments as the current method was called. That means in this example super will actually call super(argument).
Of course, you can still define a method call that explicitly sends other arguments to the original implementation, like in this example:
def method(argument)
super(argument + 1)
end
Another important edge-case is when you want to explicitly call super without any arguments although the current method was called with arguments then you need to be very explicit like this
def method(argument)
super() # note the empty parentheses
end
Let me try to describe you what I think this code does.
*args*
is using the splat operator to collect the arguments together into an array
that is totally correct, however they don't use it, and if you will go to master branch, they just changed it to *. Asking why it is defined and not used, I think that's question about bad design. They should have called it _args or at least like it is now just single splat *.
render_output is set to nil because of scopes, it has to be explicitly defined out block, lambda, proc in order to store value in it, otherwise its visibility will be locked only to those lambda, proc, block execution. Refer to this article
Benchmark.start. Blocks are great ruby construction. You are totally correct that speedtest is done, we can see it is just decorator for benchmark library.
source.
You are wondering why we cannot just pass it as Benchmark.ms(render_output), that's because what will be given to benchmark ms function? It will be given result, like <div> my html </div. And how we can measure this string result - no how. That's why we calling super right in this block, we want to access parent class function and wrap it inside block, so we are not calling it, we just construct it, and it will be called inside benchmark lib, and measured execution like
class Benchmark
...
def realtime # :yield:
r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0
end
...
end
So here we can count realtime of function execution, this is the code from original library

Ruby error - Undefined Method

I am try to write a function that will find the items in an array which match the string passed to the function. See code below.
class Island
def filter(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list = ["sasha","rory","rob","anthony","andre","tariq","kimberly","antoinette"]
list.filter("an"){|i| puts i}</i>
How i keep getting "undefined method 'filer' for #
I'm not sure what i'm doing wrong.
First let me object against the solution posted by #Sravan :
While it is true - and sometimes even a good solution - to monkey-patch a class, you have to be careful how to do it, because it may become a time bomb:
Ruby evolves, and new versions often add methods to existing classes. This means that if you add a method Array#search, and a new version of Ruby will also add a method of the same name, your new method will SILENTLY override the one in Ruby. You likely won't notice it for long time, until you are using a feature which is supposed to use Rubys Array#search - maybe by using something new in stdlib - and you get weird results. To track down this type of error can be a nightmare. This is exactly the case when you use search as a method name.
Now, how to do it then? Three possibilities:
(1) If you monkey-patch, use at least a method name which is unlikely to become part of the official interface. It might have your project's name as a prefix, or plenty of underscore characters, and so on. Note that this is not 100% foolproof: A later version of Ruby might add under the hood a private method with exactly the same name than the one you were choosing, but of course the odder your name, the less likely this will happen.
(2) If you don't like this idea of using "clumsy" names, you could at least test before defining the new method, whether it already exists, and throw an exception if it doesn't:
class Array
if self.method_defined?(:search)
raise "#{self.class}::search already defined"
else
def search(...)
...
end
end
end
(3) The third possibility is to avoid monkey-patching and keep the method in your Island class. In this case, the method definition would be different:
class Island
def self.filter(array, string)
...
end
end
and it would be called by
Island.filter(myarray, mystring)
UPDATE: Forgot a forth possibility:
(4) You can make Island a subclass of Array. I don't know what else you want to do with your islands, but maybe this is an option worth considering:
class Island < Array
def filter(string)
...
end
end
Of course, when invoking filter, you need to turn your array into an island, before you can use it:
list = Island.new([....])
Following ruby's convention over configuration, you can add/overwrite any method in any class
So, adding a function to array class makes it accessible to all the arrays. So, in this solution.
1) First thing is you have taken the filter function in Island class, instead, you need to take inside Array class since the list is an array.
class Array
def filter(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list = ["sasha","rory","rob","anthony","andre","tariq","kimberly","antoinette"]
list.filter("an"){|i| puts i}
O/P:
anthony
andre
antoinette
2) Since Filter is a keyword as suggested by other answer, take another name for it. Eg: search
class Array
def search(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list.search("an"){|i| puts i}

What is a simple / elegant way in Rails to process code only if a variable is not a Hash or String?

To check what params[:_search] is and make the good transform, I am doing :
_search = if params[:_search].is_a?(Hash)
params[:_search]
else
CGI::parse(params[:_search].to_s)
end
end
Recently I monkey path Object and wrote this part of code :
class Object
def transform_unless_kind_of(klass)
(!self.is_a?(klass) ? yield(self) : self)
end
end
params[:_search].transform_unless_kind_of(Hash) { |_self| GCI::parse(_self.to_s) }
Is there a more elegant/native way to process code only if an object is not a wanted kind ?
I think you question should be extended with details 'why there are different types coming?' and 'is there a way to avoid it?'.
Generally monkey patches should be avoided as they are usually tricky to understand.
Here you need to conditionally modify argument, and easiest and cleanest way to do it is to write a conditional statement. Further improvement depends on how often you'll need to use it in other places, how extendable it should be, etc
To use less lines you can use ternary operator, or line modifiers:
_search = params[:_search]
_search = CGI.parse(_search) unless _search.is_a?(Hash)
I tend to convert if/else structures into early returns. Something like this:
def search
_search = ensure_parsed(params[:search])
...
end
private
def ensure_parsed(search)
return search if search.is_a?(Hash)
CGI::parse(search.to_s)
end
You can even put ensure_parsed in a concern or something, and your controller code will be even cleaner and monkey-patch free.

Is there a better alternative to this Ruby idiom?

I'm finding myself writing this bit of code in my controllers a lot:
params[:task][:completed_at] = Time.parse(params[:task][:completed_at]) if params[:task][:completed_at]
Don't get hung up on what I'm doing here specifically, because the reasons change every time; but there are many circumstances where I need to check for a value in params and change it before handing it off to create or update_attributes.
Repeating params[:task][:completed_at] three times feels very bad. Is there a better way to do this?
One way to shorten this slightly is:
if c = params[:task][:completed_at]
params[:task][:completed_at] = Time.parse(c)
end
Or, you might prefer this:
params[:task][:completed_at] &&= Time.parse(params[:task][:completed_at])
In the second case, the assignment will only happen if the left side is "truthy".
I suppose you could consider doing something like this.
Implement #to_time on String and NilClass, perhaps in a extensions.rb (as recommended in Ruby Best Practices, e.g.
require 'time'
class String
def to_time
Time.parse(self) # add error/exception handling to taste
end
end
class NilClass
def to_time
nil
end
end
Then you can just call params[:task][:created_at].to_time and the duplication is gone.
I'm not at all sure that this necessarily constitutes "best practice", but IMHO it meets the objective of the question...
I am not incredibly familiar with Ruby, but since it has Perl roots, there may be a construct that allows you to write it like this:
$_ = Time->parse($_) for params[:task][:completed_at] || ();
basically exploiting the for loop to create an alias to the variable, if it exists
maybe something like:
(params[:task][:completed_at] || ()).each { |i| i = Time.parse(i) }
edit:
I see that Ruby has an alias keyword. I am not familiar enough with it to give a Ruby example, but in Perl, the above could also be written:
local *_ = \$params[$task][$completed_at];
$_ = Time->parse($_) if defined;
which specifies that $_ will be an alias for $params[$task][$completed_at]
I tried playing around with it breifly in Ruby, but didn't see a way to alias an identifier, just global variables.

What is a better way to check for a nil object before calling a method on it?

I have this method call I have to use...
financial_document.assets.length
But financial_document.assets could be nil.
I could use...
financial_document.assets.nil? ? '0' : financial_document.assets.length
Is there a less repetitive way to do that?
Dave W. Smith is on the right track.
Check this out: http://www.nach-vorne.de/2007/4/24/attr_accessor-on-steroids
One easy solution would look something like this:
class FinancialDocument
attr_accessor :assets
def assets
#assets ||= Array.new
end
...
end
Personally, I would use the or operator/keyword:
(financial_document.assets or []).length
Either way, .length is called on an array, giving you 0 if nil.
The less repetitive way of dealing with this is to ensure that financial_document.assets is always a non-null object, by arranging for it to hold an appropriate sentinel value (e.g., an empty collection, or a special object that has degenerate behavior).
See The Null Object Pattern.
Case 1:
financial_document and assets have has many relationship. In this case, financial_document.assets always returns an array. So financial_document.assets.size would give you 0 if no matching child entry is found, and size otherwise.
Case 2:
assets is just a method/attribute in financial_document.
Then have the assets method return array, so that you can always call .size on it. Just like Joel has pointed out.
In such case I use andand gem:
financial_document.assets.andand.length || 0
A more generic way to solve this class of problems is to add a try method to Object:
##
# #user.name unless #user.nil?
# vs
# #user.try(:name)
#
def try(method, *args, &block)
return nil unless method
return nil if is_a?(NilClass) and [:id, 'id'].include?(method)
self.send(method, *args, &block) if respond_to?(method)
end
I believe ruby 1.9 already has a try method on Object.
Then financial_document.assets.try(:length).to_i would achieve your desired result.
This is because nil.to_i returns 0
financial_document.assets.try(:length) || 0
try is a method that will invoke the object's method if its non nil otherwise just return nil. And try on nil methods will always return nil instead of throwing an exception.
http://api.rubyonrails.org/classes/Object.html#method-i-try
This is the Ruby way to do this!
You can do it without additional gems. I have been using ||, andand, try, but the following looks simpler. I think it is the ruby way to confirm to Dave's null object pattern.
financial_document.assets.to_a.length
This being Ruby, you could add a length method to NilClass and have it always return 0.
You can make it a bit shorter:
financial_document.assets ? financial_document.assets.length : '0'
because
financial_document.assets == !financial_document.assets.nil?
but in general, IMHO there's no less repetitive way, only various workarounds. (And this is one of the things I don't like so much in Ruby.) You can make sure that objects aren't null (as other people are suggesting here) - but you can't do that everywhere. You can wrap up the nil-checking code in helper methods or in begin-rescue blocks.
For example, rather than adding length method to nil object (which is IMHO a dirty hack), I'd wrote a helper method - a "length getter":
def fd_length(financial_document)
financial_document.assets ? financial_document.assets.length : '0'
end
Something in the model that returns 0 or the length. This keeps you from having to do a convaluted thing in your view. Things like this can normally be done in the model.
class FinancialDocument
def assets_length
assets.length.blank? 0 : assets.length
end
end

Resources