Which Ruby Memoize Pattern Does Activesupport::Memoizable Refer To

Which Ruby memoize pattern does ActiveSupport::Memoizable refer to?

Here is the commit (and subsequent discussion) where Memoizable was deprecated: https://github.com/rails/rails/commit/36253916b0b788d6ded56669d37c96ed05c92c5c

The author advocates the @foo ||= ... approach and points to this commit as an example for migration: https://github.com/rails/rails/commit/f2c0fb32c0dce7f8da0ce446e2d2f0cba5fd44b3.

Edit:
Note that I don't necessarily interpret this change as meaning that all instances of memoize can or should be replaced w/ this pattern. I read it as meaning that Memoizable is no longer needed/wanted in the Rails code itself. As the comments point out, Memoizable is much more than just a wrapper around @foo ||= .... If you need those features, go ahead and use Memoizable, you'll just have to get it from somewhere other than ActiveSupport (I'm guessing someone will fork a gem version, if they haven't already).

How do I apply a ruby memoization pattern to code this code

Use this:

def api
@api ||= @client.vm_by_name(name) if cluster
end

Note on thread safety.

How to address DEPRECATION WARNING: ActiveSupport::Memoizable is deprecated and will be removed in future releases?

I traced this down to this rails issue (specifically this comment) and this carrierwave issue.

The solution is to upgrade carrierwave:

Gemfile diff

-gem 'carrierwave', '~> 0.5.8'
+gem 'carrierwave'

Remember to run bundle update.

ActiveSupport Notification - Printing to console or log to debug?

You did not specified where you actually subscribe to the event. I would image you do it in an initializer and the rake task loads the environment so it should be fine.

To see that the block gets evaluated when the event occurs, you can use Pry, that way you can just type args and see what's inside. Put it into you Gemfile and then:

ActiveSupport::Notifications.subscribe("fb_places_updated.graph_engine") do |*args|
binding.pry
end

Run the rake task. If you did not end up in a Pry session, the event was not triggered. I'm not sure what should trigger the fb_places_updated.graph_engine event. I take it you use mongoid and as far as I know it doesn't trigger any instrumentations - there needs to be the other side:

ActiveSupport::Notifications.instrument("fb_places_updated.graph_engine", some_data: 42)

If that's the case, I wouldn't bother with notifications and just use after_update callback or an observer in the model.

ActiveSupport::Notifications should be async?

You're right. ActiveSupport::Notifications.instrument does not call the subscribers asynchronously / concurrently or anything of the sort.

If you follow the code, you'll find that #instrument calls an instrumenter, and instrumenter calls a notifier. The notifier is an instance of ActiveSupport::Notifications::Fanout, which sends notifications to all listeners via the #publish method:

def publish(name, *args)
listeners_for(name).each { |s| s.publish(name, *args) }
end

The #publish method of the listener/subscriber is not asynchronous either:

def publish(message, *args)
@delegate.call(message, *args)
end

All this does is call the block provided by the subscriber. Synchronously.

So why does the documentation claim that The block will be called asynchronously whenever someone instruments “render” and Notifications ships with a queue implementation that consumes and publish events to log subscribers in a thread?

It looks like the original implementation was asynchronous, and they forgot to update the documentation. If you look at the change history for fanout.rb, you can see some commits from 2010 where the implementation was changed to a synchronous queue. A threaded implementation was probably too complicated and error-prone. You can even see some vestiges left over:

# This is a sync queue, so there is no waiting.
def wait
end

This looks like a good candidate for a commit to docrails.

In any case, even if the implementation were asynchronous, you would probably want any long-running code to go into a queue (such as resque) and get processed by a background worker. This is so that your webapp worker processes aren't tied up processing long-running tasks, rather than serving requests.

UPDATE: Found a commit from 11 months ago that removes the incorrect information about notifications being asychronous. However, the incorrect information that the queue is running in a thread is still there.

UPDATE 2: I committed to docrails to fix the information about the queue running in a thread.

UPDATE 3: My commit has been merged into the official documentation.

uninitialized constant ActiveSupport::CoreExtensions

Checking the page you linked, I assume the problem is the following line:

format = options[:format] || ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default] || '%d %b %Y'

Looking at the file you mentioned, it appears that Rails now modifies the Date class directly rather than defining ActiveSupport::CoreExtensions::Date; furthermore, passing :default as the key to DATE_FORMATS appears to just call to_default_s on the object. The easiest way to deal with this would probably be to remove the whole reference to ActiveSupport::CoreExtensions, since the code also specifies a default:

format = options[:format] || '%d %b %Y'

You could also specify one of the date formats Rails adds in conversions.rb as so:

format = options[:format] || Date::DATE_FORMATS[:rfc822] || '%d %b %Y'

Alternatives to eval for ActiveSupport date parsing

You want time durations? I suppose you could use chronic_duration.

 my_script --mindate "1 day"
MyScript.configuration.min_date = ChronicDuration.parse(min_date_string)

But since it's natural language heuristics, it becomes not entirely well defined exactly what sorts of strings it will recognize. But it will do fancy things like "1 day and four hours".

Or you could write your own very simple parser/interpreter for the argument. Just split on a space (for "1 day" type of input) or a period (for "1.day") type of input. Recognize a few words in the second position ("hour", "minute" "day", "month", "year"), translate them to seconds, multiply the number by the translated-to-seconds word. A dozen or so lines of ruby probably.

Or you could even take advantage of the ActiveSupport feature that supports things like "1.day" to make it even easier.

  str = "11 hours"
number, unit = str.split(' ')
number.to_i.send(unit)

That would let the command line user send any method they want to a number. I'm not sure it matters. For that matter, I'm not sure if the original eval really matters or not -- but I agree with you it's bad practice. For that matter, probably so is send on user input, although not quite as bad.

Or you could just make them send in the raw number of seconds and calulcate it themselves.

my_script --mindate 86400

You realize 1.day just ends up being converted to the number of seconds in a standard day, right? I'm not sure why you're calling a number of seconds "mindate", but that's your business!

edit Or yet another alternative, make them do:

my_script --mindays 2 --minhours 4 --minminutes 3

or something.

Using rails presenters - memoizable getting deprecated in 3.1 - use ||= instead?

The ||= method is great for things that return values that evaluate as true, but it doesn't work very well for things that don't. memoize does work around this by trapping these conditions and returning accordingly. You might take an approach like this if you want to accommodate nil:

def some_method
return @some_method if (instance_variable_defined?(:"@some_method"))

@some_method = begin
...
end
end

This just checks if the variable is defined, not if it is set, which is an important distinction in your case.

I'm not sure why you think it's being deprecated [Note from Michael, it's deprecated in 3.2, see note below]. The documentation indicates it's still current in 3.1. Sometimes implementations are marked as "deprecated" when they're being moved from one module to another, but the facility remains available.



Related Topics



Leave a reply



Submit