Giter VIP home page Giter VIP logo

memoist's Introduction

Memoist

Build Status

Memoist is an extraction of ActiveSupport::Memoizable.

Since June 2011 ActiveSupport::Memoizable has been deprecated. But I love it, and so I plan to keep it alive.

Usage

Just extend with the Memoist module

require 'memoist'
class Person
  extend Memoist

  def social_security
    puts "execute!"
    decrypt_social_security
  end
  memoize :social_security
end

person = Person.new

person.social_security
# execute!
# => (returns decrypt_social_security)

person.social_security
# => (returns the memoized value)

And person.social_security will only be calculated once.

Every memoized function (which initially was not accepting any arguments) has a (reload) argument you can pass in to bypass and reset the memoization:

def some_method
  Time.now
end
memoize :some_method

Calling some_method will be memoized, but calling some_method(true) will rememoize each time.

You can even memoize method that takes arguments.

class Person
  def taxes_due(income)
    income * 0.40
  end
  memoize :taxes_due
end

This will only be calculated once per value of income.

You can also memoize class methods.

class Person

  class << self
    extend Memoist
    def with_overdue_taxes
      # ...
    end
    memoize :with_overdue_taxes
  end

end

When a sub-class overrides one of its parent's methods and you need to memoize both. Then you can use the :identifier parameter in order to help Memoist distinguish between the two.

class Clock
  extend Memoist
  def now
     "The time now is #{Time.now.hour} o'clock and #{Time.now.min} minutes"
  end
  memoize :now
end

class AccurateClock < Clock
  extend Memoist
  def now
    "#{super} and #{Time.now.sec} seconds"
  end
  memoize :now, :identifier => :accurate_clock
end

Reload

Each memoized function comes with a way to flush the existing value.

person.social_security       # returns the memoized value
person.social_security(true) # bypasses the memoized value and rememoizes it

This also works with a memoized method with arguments

person.taxes_due(100_000)       # returns the memoized value
person.taxes_due(100_000, true) # bypasses the memoized value and rememoizes it

If you want to flush the entire memoization cache for an object

person.flush_cache   # returns an array of flushed memoized methods, e.g. ["social_security", "some_method"]

Authors

Everyone who contributed to it in the rails repository.

  • Joshua Peek
  • Tarmo Tänav
  • Jeremy Kemper
  • Eugene Pimenov
  • Xavier Noria
  • Niels Ganser
  • Carl Lerche & Yehuda Katz
  • jeem
  • Jay Pignata
  • Damien Mathieu
  • José Valim
  • Matthew Rudy Jacobs

Contributing

  1. Fork it ( https://github.com/matthewrudy/memoist/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

License

Released under the MIT License, just as Ruby on Rails is.

memoist's People

Contributors

a-chernykh avatar biow0lf avatar brandondrew avatar byroot avatar fervic avatar fny avatar joemcb avatar joshuapinter avatar jrafanie avatar matiaskorhonen avatar matthewrudy avatar olivierlacan avatar pboling avatar pikachuexe avatar sebjacobs avatar unasuke avatar zachhale avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

memoist's Issues

Delegation with memoize

Hi, guys!
Can I delegate with memoization? Without rewriting method of target object:
Something like that:

delegate :foo, to: :bar, memoize: true

Or, please, show this functionality is :)

Add an interface to write to memoized ivar?

Hi, sometimes we need to batch preload attributes like:

ivar = Memoist.memoized_ivar_for(:available_stock)
products.each do |product|
  product.instance_variable_set(ivar, xxx)
end

would it be great to be

products.each do |product|
  Memoist.set_memoized(product, :available_stock, xxx)
end

or something?

Thanks in advanced.

Is Memoist Thread Safe?

I'm trying to make my app thread-safe to use the Puma server.

I'm using this in my payments code and don't want to get this wrong!

app/models/concerns/user_stripe_payments_module.rb:8: memoize :payment_profile # reload and rememoizes with #payment_profile(true)
app/models/concerns/user_stripe_payments_module.rb:15: def rememoize_payment_profile

Memory leaks when memoizing class methods

Hi,

I'd like to confirm my understanding about how class method memoization works in order to establish a best practice for myself and hopefully will be useful for others as well.

Here's an example ...

class Person

class << self
extend Memoist
def with_overdue_taxes(arg)
# ...
end
memoize :with_overdue_taxes
end

end

If arg is an integer that varies between 1 to 1 million, that means I am potentially keeping 1 million cached entries in memory. And this won't be garbage collected because it's referenced by the class, so the server will run out of memory quickly.

Is this right?

If so, then perhaps it's worth mentioning this in the README to help fellow programmers avoid this.

Thanks

Memoizable fields are run on destroy

This is regarding the discussion on 35fe922.

This is with the latest version of memoist and Rails 3.2.12

# ruby script named test_destroy.rb
require "rubygems"
require "active_record"
require "sqlite3"
require "memoist"
require "debugger"

require "fileutils"

database_config = {
  adapter: "sqlite3",
  database: "test-freeze.sqlite3"
}

FileUtils.rm(database_config[:database]) if File.exist?(database_config[:database])
ActiveRecord::Base.establish_connection(database_config)

class CreatePerson < ActiveRecord::Migration
  def change
    create_table :people do |t|
      t.string :name
    end
  end
end


CreatePerson.migrate(:up)

class Person < ActiveRecord::Base
  extend Memoist

  def foo
    puts "shouldn't be printed"
  end
  memoize :foo
end

person = Person.create!(name: "Name")
person.update_attributes(name: "Name updated")
person.destroy

Output:

∴ ruby test_destroy.rb 
==  CreatePerson: migrating ===================================================
-- create_table(:people)
   -> 0.0070s
==  CreatePerson: migrated (0.0071s) ==========================================

shouldn't be printed

Issue with extract_reload method and optional arguments

I've just encountered this weird issue where my method is doing something unexpected.
It seems to be related to extract_reload! method of Memoize.

This method can be found on https://github.com/matthewrudy/memoist/blob/master/lib/memoist.rb#L53.

Here is the code in question

if args.length == method.arity.abs + 1 && (args.last == true || args.last == :reload)
  reload = args.pop
end

It removes the last arguments from the args if it is either true or `:reload which seemingly to support the reloading of the method when it is desired.

However, the problem occurs if you have more than one optional arguments

def blah(first_arg = 'something, second_arg = true)
  #do something
end

Memoize will happily remove that second_arg thinking it is the flag for the reload when it is not so. second_arg will always be true in this case because of that behaviour of Memoize.

I can see that this is because Method#arity returns -1 in this case as there is no required arguments.
I think it's unreliable to rely on arity to determine whether you can safely assume the last argument is the optional value meant for Memoize.

Obviously, I can work around this issue, but this would cause all sorts of weird issues for unsuspecting users so it'd be good to have it fixed.
I'd appreciate if anyone has any thoughts on this?

Thanks,

It looks possible to call the value-to-be-cached twice

Related: #45

Consider the plain ruby memoization pattern:

      def foo
        @foo ||= (...)
      end

This looks thread-unsafe, because two concurrent threads which call foo for the first time at the same time could trigger the code in (...). Only one would 'win', and everything would normally from then on, but (...) has been called twice.

If that was an expensive operation, it would have been called twice (suboptimal). If it was an operation with side-effects, we might encounter actual problems.

From my understanding of memoist.rb, this doesn't seem prevented at all.

What do you think - is there room for improvement here?

Cheers - Victor

Provide public interface for unmemoized versions of methods

I think it would be nice to have a public functions for unmemoized versions. I know that currently I can use _unmemoized_my_function_name, but it's name started with _ which makes it kind of private.

Can we remove leading underscore and add the instruction how to call original function in README? I can wrap this into simple PR if my idea make sense.

Optional value not supported for last argument?

I have a method defined this way:

def latest_duty_tax(cat, country_code=@country)

end
memoize :latest_duty_tax

It works with ActiveSupport::Memoizable but triggers an exception with Memoist:

sidr.latest_duty_tax(cat, country_code, :reload)
# =>
#ArgumentError:   wrong number of arguments (3 for 2)
#/app/services/shipping_item_data_retriever.rb:24:in `latest_duty_tax'
#/.rvm/gems/ruby-1.9.3-p545@ector/gems/memoist-0.9.3/lib/memoist.rb:174:in `latest_duty_tax'

Nice declarative memoization in Ruby 2.1

In Ruby 2.1, def returns the symbolized method name. Therefore, Ruby 2.1 users should be able to do the following, which I find pleasant:

memoize def expensive_calc_1; ...; end
memoize def expensive_calc_2
  lots * of * expensive * things
end

I haven't tested, but it should work, and I thought it might be interesting to call it out in the docs if so.

Cheers.

Odd reload behavior with optional args

It seems to me that passing true here should cause the ivar to continue growing?

$ pry
[1] pry(main)> require 'memoist'
=> true
[2] pry(main)> class Thing
[2] pry(main)*   extend Memoist
[2] pry(main)*   def grow(opts = {})
[2] pry(main)*     @num ||= 0
[2] pry(main)*     p 'growing'
[2] pry(main)*     @num += 1
[2] pry(main)*   end
[2] pry(main)*   memoize :grow
[2] pry(main)* end
=> :grow
[3] pry(main)> thing = Thing.new
=> #<Thing:0x007f90c001b938>
[4] pry(main)> thing.grow
"growing"
=> 1
[5] pry(main)> thing.grow
=> 1
[6] pry(main)> thing.grow(true)
"growing"
=> 2
[7] pry(main)> thing.grow(true)
=> 2
[8] pry(main)> thing.grow(true)
=> 2

Like so:

$ pry
[1] pry(main)> require 'memoist'
=> true
[2] pry(main)> class Thing
[2] pry(main)*   extend Memoist
[2] pry(main)*   def grow(opts)
[2] pry(main)*     @num ||= 0
[2] pry(main)*     p 'growing'
[2] pry(main)*     @num += 1
[2] pry(main)*   end
[2] pry(main)*   memoize :grow
[2] pry(main)* end
=> :grow
[3] pry(main)> thing = Thing.new
=> #<Thing:0x007f9e42ba2bc8>
[4] pry(main)> thing.grow({})
"growing"
=> 1
[5] pry(main)> thing.grow({})
=> 1
[6] pry(main)> thing.grow({}, true)
"growing"
=> 2
[7] pry(main)> thing.grow({}, true)
"growing"
=> 3
[8] pry(main)> thing.grow({}, true)
"growing"
=> 4
[9] pry(main)> thing.grow({})
=> 4
[10] pry(main)> thing.grow({hi: true})
"growing"
=> 5
[11] pry(main)> thing.grow({hi: true})
=> 5
[12] pry(main)> thing.grow({hi: true}, true)
"growing"
=> 6

Add changelog?

We like keeping our gems up to date, but when upgrading Memoist it's hard to know what the changes are between gem versions, and especially if there are backwards-incompatible changes between versions. A CHANGELOG.md file would be extremely helpful.

[Question] Why store 0-arity method caches as arrays?

I've been looking through the code a bit and I'm curious why instance methods with no arguments are stored as single-element arrays, and retrieving from the cache is just returning the first element of the array, and wiping the cache is emptying the array.

It seems like a more intuitive approach would be to declare the instance variable to equal the cached result directly, and have flush_cache just call remove_instance_variable. That also may be more efficient (no Ruby array overhead) but it's probably so minor as to not matter much.

Purely asking out of curiosity.

Global cache flush

Is there a way to globally flush the cache?

I have some nasty order dependent bugs in my rspecs where objects with their caches seem to be leaking from one spec to another.

So far, the best workaround I came up with is this

ObjectSpace.each_object { |o| o.flush_cache if o.respond_to? :flush_cache }

horrible workaround in my spec/spec_helper.rb.

Can we do better?

{Question} Time-based expiry mechanism

Hi, I was wondering if you think this library would benefit from time-based cache invalidation mechanism expressed in seconds, something in lines of

    memoize :sleep, expire_after: 60

Basically, if expire_after key is found in method_names (as part of last Hash argument), there would be another variable to do a bookkeeping for, last time certain method was called (possibly with some arguments) and it's cache was skipped. When skipped, that time would be bumped to new Time.now value. That check be performed when resolving skip_cache variable, so that if last_access_time + expire_after <= Time.now skip_cache should be resolved to true, at least from that angle.

I could create PR + tests for that if you think its worthwhile, but preliminary benchmark results do show as much as 25-35% performance degradation (kinda expected, as there are some additional time-based checks). Maybe splitting implementation in memoize + timed_memoize/expirable_memoize could solve this and at least constrain performance degradation to methods memoized with this new approach (old methods would remain as-is).

I know this would increase code surface size or decrease perf (maybe there is a way to mitigate both 🤔 ), hence the question 😄

model_instance.reload doesn't flush cache

I would expect that calling #reload on an instance of an ActiveRecord model would flush the cache.

If I have a table named foos with one string column name:

class Foo < ActiveRecord::Base
  extend Memoist

  memoize def change_name!
    name = 'bar'
    save
    name
  end
end
describe Foo do
  describe '#change_name!' do
    subject { Foo.create(name: '') }

    expect { subject.change_name! }.to change { subject.reload.name }from('').to('bar')
  end
end

The above test fails unless #flush_cache is called on subject in the change block. Is this the intended behavior?

v0.13 changed #unmemoize_all behavior with subclasses

The performance changes in v0.13 look really nice, but it seems to have caused a regression issue in the Rails app I am working on.

Here's a contrived example:

class Fruit
  extend Memoist
  attr_accessor :name, :calories

  def initialize(name, calories)
    @name = name
    @calories = calories
  end

  def generic_description
    "#{name}: #{calories}"
  end
  memoize :generic_description
end

class Apple < Fruit
  def initialize
    @name = "apple"
    @calories = 50
  end

  def specific_description
    "An #{name} a day keeps the doctor away!"
  end
  memoize :specific_description
end

IRB output:

apple = Apple.new #=> #<Apple:0x007fb77502b960 @name="apple", @calories=50>
apple.generic_description == "apple: 50" #=> true
apple.specific_description == "An apple a day keeps the doctor away!" #=> true
apple.name = "Granny smith apple" #=> "Granny smith apple"
apple.unmemoize_all #=> #<Set: {:specific_description}>
apple.generic_description == "apple: 50" #=> true
apple.specific_description == "An Granny smith apple a day keeps the doctor away!" #=> true

The main issue I want to highlight is the set of methods returned after apple.unmemoize_all.
If I do the same example with v0.12:

apple = Apple.new #=> #<Apple:0x007fb51bcd0640 @name="apple", @calories=50>
apple.generic_description == "apple: 50" #=> true
apple.specific_description == "An apple a day keeps the doctor away!" #=> true
apple.name = "Granny smith apple" #=> "Granny smith apple"
apple.unmemoize_all #=> ["specific_description", "generic_description"]
apple.generic_description == "apple: 50" #=> false
apple.specific_description == "An Granny smith apple a day keeps the doctor away!" #=> true

Here, apple.unmemoize_all returns the full list.

If I am seeing the issue correctly, it is because the memoized method list is stored at the class level

Currently, I have reverted back to v0.12 just to get my build green. I believe it is possible to work around this issue by just passing in the methods manually with flush_cache. but it is not ideal.

Interest in def_memoized?

I was thinking of writing a gem to do this and then discovered Memoist (looks nice, good work!) and figured it may be better to just extend this if there's interest. Not having used Memoist though I defer to the maintainers of course. My proposal is this syntax:

require 'memoist'
class Person
  extend Memoist

  def_memoized social_security
    decrypt_social_security
  end
end

I'd be happy to write a PR for this but there's a lot about Memoist I don't know, so let me know if you guys think this is viable/desirable.

Name error when calling query methods

Hi,

Since moving to memoist 0.16.1 we started getting this error:
C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/memoist-0.16.1/lib/memoist.rb:140:in alias_method': undefined method exists_query' for class `Stacker::Stack' (NameError)

It looks like this line is causing errors:
https://github.com/ansarada/stacker/blob/master/lib/stacker/stack.rb#L29

when memoist is being called here:
https://github.com/ansarada/stacker/blob/master/lib/stacker/stack.rb#L66

I think the trailing "?" is causing issues with the latest version of memosit

Warning when class memoizes multiple methods

When loading a class like this:

class Jacob
  extend Memoist

  def foo
    true
  end
  memoize :foo

  def bar
    false
  end
  memoize :bar
end

I see warnings like this:

/Users/jacobevelyn/.gem/ruby/2.3.0/gems/memoist-0.14.0/lib/memoist.rb:118: warning: method redefined; discarding old memoized_methods
/Users/jacobevelyn/.gem/ruby/2.3.0/gems/memoist-0.14.0/lib/memoist.rb:118: warning: previous definition of memoized_methods was here

I know why they're appearing (the second memoize call is redefining memoized_methods) and I know it's not a bad thing; just noting that it's a little scary to see if you don't know why it's happening.

Hash parameters not supported

I just tried this:

def foo(bar: bar)
  ...
end
memoize :foo

and received errors. I haven't gotten a chance to look into it much yet and I'd be happy to write a fix; just wanted to make sure this was recorded somewhere so I didn't forget about it :)

Cannot run Specs anymore

I included memoist in one of my models. Since then my specs do not run anymore. But the application itself has no problems (it can be accessed via web or console).

My Code:

require "memoist"

class Book < ActiveRecord::Base
  include ApplicationHelper
  include Memoize

  belongs_to :spot
  def test
  ...
  end
  memoize :test  
  ...
end 

The errormessage is the following:

book.rb:5:in `<class:Book>': uninitialized constant Book::Memoize (NameError)
    from /home/MrX/TestApp/RoR/application/app/models/book.rb:3:in `<top (required)>'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/activesupport-3.2.11/lib/active_support/inflector/methods.rb:230:in `block in constantize'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/activesupport-3.2.11/lib/active_support/inflector/methods.rb:229:in `each'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/activesupport-3.2.11/lib/active_support/inflector/methods.rb:229:in `constantize'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/activesupport-3.2.11/lib/active_support/inflector/methods.rb:260:in `safe_constantize'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/activesupport-3.2.11/lib/active_support/core_ext/string/inflections.rb:66:in `safe_constantize'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/actionpack-3.2.11/lib/action_controller/metal/params_wrapper.rb:152:in `_default_wrap_model'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/actionpack-3.2.11/lib/action_controller/metal/params_wrapper.rb:169:in `_set_wrapper_defaults'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/actionpack-3.2.11/lib/action_controller/metal/params_wrapper.rb:133:in `inherited'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/actionpack-3.2.11/lib/abstract_controller/railties/routes_helpers.rb:7:in `block (2 levels) in with'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/actionpack-3.2.11/lib/action_controller/railties/paths.rb:7:in `block (2 levels) in with'
    from /home/MrX/TestApp/RoR/application/app/controllers/book_controller.rb:2:in `<top (required)>'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/engine.rb:439:in `block (2 levels) in eager_load!'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/engine.rb:438:in `each'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/engine.rb:438:in `block in eager_load!'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/engine.rb:436:in `each'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/engine.rb:436:in `eager_load!'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/application/finisher.rb:53:in `block in <module:Finisher>'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/initializable.rb:30:in `instance_exec'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/initializable.rb:30:in `run'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/initializable.rb:55:in `block in run_initializers'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/initializable.rb:54:in `each'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/initializable.rb:54:in `run_initializers'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/application.rb:136:in `initialize!'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/railties-3.2.11/lib/rails/railtie/configurable.rb:30:in `method_missing'
    from /home/MrX/TestApp/RoR/application/config/environment.rb:5:in `<top (required)>'
    from /home/MrX/TestApp/RoR/application/spec/spec_helper.rb:5:in `require'
    from /home/MrX/TestApp/RoR/application/spec/spec_helper.rb:5:in `<top (required)>'
    from /home/MrX/TestApp/RoR/application/spec/controllers/tasks_controller_spec.rb:1:in `require'
    from /home/MrX/TestApp/RoR/application/spec/controllers/tasks_controller_spec.rb:1:in `<top (required)>'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/rspec-core-2.12.2/lib/rspec/core/configuration.rb:789:in `load'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/rspec-core-2.12.2/lib/rspec/core/configuration.rb:789:in `block in load_spec_files'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/rspec-core-2.12.2/lib/rspec/core/configuration.rb:789:in `each'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/rspec-core-2.12.2/lib/rspec/core/configuration.rb:789:in `load_spec_files'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/rspec-core-2.12.2/lib/rspec/core/command_line.rb:22:in `run'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/rspec-core-2.12.2/lib/rspec/core/runner.rb:80:in `run'
    from /home/MrX/.rvm/gems/ruby-1.9.3-p374/gems/rspec-core-2.12.2/lib/rspec/core/runner.rb:17:in `block in autorun'

Is this issue already known? Unfortunately I have no solution right now.

Support reloading a file while in a console

If I make a change to a file and want to reload it in my pry session I get AlreadyMemoizedError. It would be great if there was a simple way to call load '...' on a file that contains memoize and have it work.

Memoization broken for optional args

Memoization is currently broken for methods with optional args. The piece of code under question is:

if args.length == method.arity.abs + 1 && (args.last == true || args.last == :reload)
  reload = args.pop

There are a couple of things wrong with the this piece of code.

  1. method.arity only represents total number of positional args. 1 is added and the arity is negated if there is an optional/named argument. We should not be depending on method arity and picking a particular positional argument because it's impossible to tell whether the intent was to pass a value for the optional arg or true flag to memoist.
    e.g.
def self.foo(a, b=1, c=2, d=3, e:4)
end

In this case the arity of foo will be -2.

  1. Because of what's mentioned above, this not only misses the reload flag but also incorrectly modifies the arguments of a method that was only passed a true value for one of the optional args.

We should use a special named parameter memoist_reload always and not depend on arity at all. We could still use arity when it's positive for backward compatibility (because that's the only case when memoization is actually working even now.).

Place sample code for execution in README.md

I tried Usage and I thought that it would be better to write how to check the operation of memoization.

e.g.

require 'memoist'

class Example
  extend Memoist

  def some_method
    puts "execute!"
    Time.now
  end
  memoize :some_method
end

example = Example.new

example.some_method
# execute!
# => 2018-03-26 14:43:39 +0000

example.some_method
# => 2018-03-26 14:43:39 +0000

Class method memoization and flushing

The documentation doesn't say about flushing of memoized class methods.
I determined, that the following works:

Person.flush_cache

But is it possible to automatically flush the class method on every instance changes (create, update, delete)?

Flush Memoized Class Methods

Given a class:

  class Foo
    class << self
      extend Memoist

      def bar
        "hello"
      end
      memoize :bar

    end
  end

Foo.flush_cache spits out:

NoMethodError: undefined method `all_memoized_structs' for Foo:Class

I also found this related issue:
#54

add DSL

It will be cool to implement DSL for duplications reduce.

class Example
  extend Memoist

  mem :method_name do |args|
  end
end

Docs for #memoize_all?

Hi there.

I was looking for a declarative way to memoize all methods in my presenter/decorator. I came across memoize_all, but see no docs, and haven't been able to get it to work in testing.

What is the intended functionality of that method?

Thanks.

Garbage Collection?

How do you deal with garbage collection? I couldn't see anywhere in your docs, and the code got a big convoluted while tracing it for me to discern. Basically, do you free the memory here:

class Person
  def taxes_due(income)
    income * 0.40
  end
  memoize :taxes_due
end

a = Person.new
a.taxes_due(100_000)
a = nil

or will it hold on to that memoized value for the whole life of the program?

Similarly, if you take a parameter and then the last reference to the param goes away, will you still hold on to it? Like:

class Foo
  def bar(baz)
    "1"
  end
  memoize :bar
end

a = Foo.new
a.bar(Object.new)

Calling `memoize` on methods whose names are certain symbols causes syntax errors

For example:

require 'memoist'

class Foo
  extend Memoist

  def [](bar)
    rand
  end
  memoize :[]
end
[22:03:27] ~/projects  ➜ ruby brackets.rb
Traceback (most recent call last):
        8: from brackets.rb:3:in `<main>'
        7: from brackets.rb:9:in `<class:Foo>'
        6: from /Users/pat/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/memoist-0.16.2/lib/memoist.rb:127:in `memoize'
        5: from /Users/pat/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/memoist-0.16.2/lib/memoist.rb:127:in `each'
        4: from /Users/pat/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/memoist-0.16.2/lib/memoist.rb:131:in `block in memoize'
        3: from /Users/pat/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/memoist-0.16.2/lib/memoist.rb:55:in `memoist_eval'
        2: from /Users/pat/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/memoist-0.16.2/lib/memoist.rb:55:in `class_eval'
        1: from /Users/pat/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/memoist-0.16.2/lib/memoist.rb:205:in `block (2 levels) in memoize'
/Users/pat/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/memoist-0.16.2/lib/memoist.rb:205:in `module_eval': /Users/pat/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/memoist-0.16.2/lib/memoist.rb:213: syntax error, unexpected '(', expecting `end' (SyntaxError)
...        value = _unmemoized_[](*args)
...                              ^

This is true also of methods such as +, +@, -, -@, &, |, etc.

Memoizable causes annotate gem to fail

When running annotate, it causes the model not to be annotated when it includes Memoizable and uses it on a method.

In the output:

Unable to annotate user.rb: The method source_uris is already memoized

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.