memery 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.travis.yml +0 -8
- data/CHANGELOG.md +11 -1
- data/Gemfile +0 -2
- data/README.md +73 -0
- data/Rakefile +7 -1
- data/benchmark.rb +46 -0
- data/lib/memery.rb +35 -20
- data/lib/memery/version.rb +1 -1
- data/memery.gemspec +3 -0
- metadata +45 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: b422cc5336406f390272a02aea253b991470519a786844cb72b4cc0c05ef3b56
         | 
| 4 | 
            +
              data.tar.gz: 12c2329a8539305d85dcb99b30604e55aa04b0f75153fc6a173ae73875f423c8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 94b69a032837511e10b368ab6db8212bf6b48c8b835c5f76732484b05dea8910af17712797a42fa334ee0529973f8c61d69ea052c1949619411bc1ae7c0897fa
         | 
| 7 | 
            +
              data.tar.gz: fd17f734226f87487223034bf07afae259ed54ca5b216bfc11322b38adcdf1dd3898bf0f238ab8dcb846492c7f93baa8e51d909584bd1ec1e8b07774d4cc3693
         | 
    
        data/.rspec
    CHANGED
    
    
    
        data/.travis.yml
    CHANGED
    
    | @@ -11,15 +11,7 @@ rvm: | |
| 11 11 |  | 
| 12 12 | 
             
            before_install: gem install bundler
         | 
| 13 13 |  | 
| 14 | 
            -
            env: SUITE="rspec"
         | 
| 15 | 
            -
             | 
| 16 | 
            -
            script: bundle exec $SUITE
         | 
| 17 | 
            -
             | 
| 18 14 | 
             
            matrix:
         | 
| 19 15 | 
             
              fast_finish: true
         | 
| 20 | 
            -
              # Only run RuboCop on the latest Ruby
         | 
| 21 | 
            -
              include:
         | 
| 22 | 
            -
                - rvm: 2.6
         | 
| 23 | 
            -
                  env: SUITE="rubocop"
         | 
| 24 16 | 
             
              allow_failures:
         | 
| 25 17 | 
             
                - rvm: ruby-head
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,11 @@ | |
| 1 1 | 
             
            ## [Unreleased]
         | 
| 2 2 |  | 
| 3 | 
            +
            ## [1.2.0] - 2019-10-19
         | 
| 4 | 
            +
            ### Added
         | 
| 5 | 
            +
            - Add `:ttl` option for `memoize` method ([@AlexWayfer]) [#11]
         | 
| 6 | 
            +
            - Add benchmark script ([@AlexWayfer]) [#14]
         | 
| 7 | 
            +
            - Add `.memoized?` method ([@AlexWayfer]) [#17]
         | 
| 8 | 
            +
             | 
| 3 9 | 
             
            ## [1.1.0] - 2019-08-05
         | 
| 4 10 | 
             
            ### Fixed
         | 
| 5 11 | 
             
            - Optimize speed and memory for cached values returns. ([@AlexWayfer]) [#10]
         | 
| @@ -20,7 +26,8 @@ | |
| 20 26 | 
             
            [0.6.0]: https://github.com/tycooon/memery/compare/v0.5.0...v0.6.0
         | 
| 21 27 | 
             
            [1.0.0]: https://github.com/tycooon/memery/compare/v0.6.0...v1.0.0
         | 
| 22 28 | 
             
            [1.1.0]: https://github.com/tycooon/memery/compare/v1.0.0...v1.1.0
         | 
| 23 | 
            -
            [ | 
| 29 | 
            +
            [1.2.0]: https://github.com/tycooon/memery/compare/v1.1.0...v1.2.0
         | 
| 30 | 
            +
            [Unreleased]: https://github.com/tycooon/memery/compare/v1.2.0...HEAD
         | 
| 24 31 |  | 
| 25 32 | 
             
            [@tycooon]: https://github.com/tycooon
         | 
| 26 33 | 
             
            [@AlexWayfer]: https://github.com/AlexWayfer
         | 
| @@ -28,3 +35,6 @@ | |
| 28 35 | 
             
            [#3]: https://github.com/tycooon/memery/pull/3
         | 
| 29 36 | 
             
            [#7]: https://github.com/tycooon/memery/pull/7
         | 
| 30 37 | 
             
            [#10]: https://github.com/tycooon/memery/pull/10
         | 
| 38 | 
            +
            [#11]: https://github.com/tycooon/memery/pull/11
         | 
| 39 | 
            +
            [#14]: https://github.com/tycooon/memery/pull/14
         | 
| 40 | 
            +
            [#17]: https://github.com/tycooon/memery/pull/17
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -62,6 +62,25 @@ a.call { 1 } # => 42 | |
| 62 62 | 
             
            # Will print because passing a block disables memoization
         | 
| 63 63 | 
             
            ```
         | 
| 64 64 |  | 
| 65 | 
            +
            Methods with arguments are supported and the memoization will be done based on arguments using an internal hash. So this will work as expected:
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            ```ruby
         | 
| 68 | 
            +
            class A
         | 
| 69 | 
            +
              include Memery
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              memoize def call(arg1, arg2)
         | 
| 72 | 
            +
                puts "calculating"
         | 
| 73 | 
            +
                arg1 + arg2
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
            end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            a = A.new
         | 
| 78 | 
            +
            a.call(1, 5) # => 6
         | 
| 79 | 
            +
            a.call(2, 15) # => 17
         | 
| 80 | 
            +
            a.call(1, 5) # => 6
         | 
| 81 | 
            +
            # Text will be printed only twice, once per unique argument list.
         | 
| 82 | 
            +
            ```
         | 
| 83 | 
            +
             | 
| 65 84 | 
             
            For class methods:
         | 
| 66 85 |  | 
| 67 86 | 
             
            ```ruby
         | 
| @@ -117,6 +136,60 @@ a.call # => 42 | |
| 117 136 | 
             
            # with `true` result of condition block.
         | 
| 118 137 | 
             
            ```
         | 
| 119 138 |  | 
| 139 | 
            +
            For memoization with time-to-live:
         | 
| 140 | 
            +
             | 
| 141 | 
            +
            ```ruby
         | 
| 142 | 
            +
            class A
         | 
| 143 | 
            +
              include Memery
         | 
| 144 | 
            +
             | 
| 145 | 
            +
              def call
         | 
| 146 | 
            +
                puts "calculating"
         | 
| 147 | 
            +
                42
         | 
| 148 | 
            +
              end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
              memoize :call, ttl: 3 # seconds
         | 
| 151 | 
            +
            end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
            a = A.new
         | 
| 154 | 
            +
            a.call # => 42
         | 
| 155 | 
            +
            # calculating
         | 
| 156 | 
            +
            a.call # => 42
         | 
| 157 | 
            +
            a.call # => 42
         | 
| 158 | 
            +
            # Text will be printed again only after 3 seconds of time-to-live.
         | 
| 159 | 
            +
            # 3 seconds later...
         | 
| 160 | 
            +
            a.call # => 42
         | 
| 161 | 
            +
            # calculating
         | 
| 162 | 
            +
            a.call # => 42
         | 
| 163 | 
            +
            a.call # => 42
         | 
| 164 | 
            +
            # another 3 seconds later...
         | 
| 165 | 
            +
            a.call # => 42
         | 
| 166 | 
            +
            # calculating
         | 
| 167 | 
            +
            a.call # => 42
         | 
| 168 | 
            +
            a.call # => 42
         | 
| 169 | 
            +
            ```
         | 
| 170 | 
            +
             | 
| 171 | 
            +
            Check if method is memoized:
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            ```ruby
         | 
| 174 | 
            +
            class A
         | 
| 175 | 
            +
              include Memery
         | 
| 176 | 
            +
             | 
| 177 | 
            +
              memoize def call
         | 
| 178 | 
            +
                puts "calculating"
         | 
| 179 | 
            +
                42
         | 
| 180 | 
            +
              end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
              def execute
         | 
| 183 | 
            +
                puts "non-memoized"
         | 
| 184 | 
            +
              end
         | 
| 185 | 
            +
            end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
            a = A.new
         | 
| 188 | 
            +
             | 
| 189 | 
            +
            a.memoized?(:call) # => true
         | 
| 190 | 
            +
            a.memoized?(:execute) # => false
         | 
| 191 | 
            +
            ```
         | 
| 192 | 
            +
             | 
| 120 193 | 
             
            ## Difference with other gems
         | 
| 121 194 | 
             
            Memery is very similar to [Memoist](https://github.com/matthewrudy/memoist). The difference is that it doesn't override methods, instead it uses Ruby 2 `Module.prepend` feature. This approach is cleaner (for example you are able to inspect the original method body using `method(:x).super_method.source`) and it allows subclasses' methods to work properly: if you redefine a memoized method in a subclass, it's not memoized by default, but you can memoize it normally (without using awkward `identifier: ` argument) and it will just work:
         | 
| 122 195 |  | 
    
        data/Rakefile
    CHANGED
    
    | @@ -2,7 +2,13 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require "bundler/gem_tasks"
         | 
| 4 4 | 
             
            require "rspec/core/rake_task"
         | 
| 5 | 
            +
            require "rubocop/rake_task"
         | 
| 5 6 |  | 
| 6 7 | 
             
            RSpec::Core::RakeTask.new(:spec)
         | 
| 8 | 
            +
            RuboCop::RakeTask.new(:lint)
         | 
| 7 9 |  | 
| 8 | 
            -
            task default:  | 
| 10 | 
            +
            task default: %i[lint spec]
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            task :benchmark do
         | 
| 13 | 
            +
              require_relative "./benchmark"
         | 
| 14 | 
            +
            end
         | 
    
        data/benchmark.rb
    ADDED
    
    | @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "bundler/setup"
         | 
| 4 | 
            +
            Bundler.setup
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require "benchmark"
         | 
| 7 | 
            +
            require "benchmark/ips"
         | 
| 8 | 
            +
            require "benchmark/memory"
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            puts "```ruby"
         | 
| 11 | 
            +
            puts File.read(__FILE__)
         | 
| 12 | 
            +
            puts "```"
         | 
| 13 | 
            +
            puts
         | 
| 14 | 
            +
            puts "### Output"
         | 
| 15 | 
            +
            puts
         | 
| 16 | 
            +
            puts "```"
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            require_relative "lib/memery"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            class Foo
         | 
| 21 | 
            +
              class << self
         | 
| 22 | 
            +
                include Memery
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def base_find(char)
         | 
| 25 | 
            +
                  ("a".."k").find { |letter| letter == char }
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                memoize def find_new(char)
         | 
| 29 | 
            +
                  base_find(char)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            def test_memery
         | 
| 35 | 
            +
              Foo.find_new("d")
         | 
| 36 | 
            +
            end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            Benchmark.ips do |x|
         | 
| 39 | 
            +
              x.report("test_memery") { test_memery }
         | 
| 40 | 
            +
            end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            Benchmark.memory do |x|
         | 
| 43 | 
            +
              x.report("test_memery") { 100.times { test_memery } }
         | 
| 44 | 
            +
            end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            puts "```"
         | 
    
        data/lib/memery.rb
    CHANGED
    
    | @@ -3,26 +3,39 @@ | |
| 3 3 | 
             
            require "memery/version"
         | 
| 4 4 |  | 
| 5 5 | 
             
            module Memery
         | 
| 6 | 
            -
               | 
| 7 | 
            -
                base | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 6 | 
            +
              class << self
         | 
| 7 | 
            +
                def included(base)
         | 
| 8 | 
            +
                  base.extend(ClassMethods)
         | 
| 9 | 
            +
                  base.include(InstanceMethods)
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def method_visibility(klass, method_name)
         | 
| 13 | 
            +
                  case
         | 
| 14 | 
            +
                  when klass.private_method_defined?(method_name)
         | 
| 15 | 
            +
                    :private
         | 
| 16 | 
            +
                  when klass.protected_method_defined?(method_name)
         | 
| 17 | 
            +
                    :protected
         | 
| 18 | 
            +
                  when klass.public_method_defined?(method_name)
         | 
| 19 | 
            +
                    :public
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 10 22 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
                when klass.private_method_defined?(method_name)
         | 
| 14 | 
            -
                  :private
         | 
| 15 | 
            -
                when klass.protected_method_defined?(method_name)
         | 
| 16 | 
            -
                  :protected
         | 
| 17 | 
            -
                when klass.public_method_defined?(method_name)
         | 
| 18 | 
            -
                  :public
         | 
| 23 | 
            +
                def monotonic_clock
         | 
| 24 | 
            +
                  Process.clock_gettime(Process::CLOCK_MONOTONIC)
         | 
| 19 25 | 
             
                end
         | 
| 20 26 | 
             
              end
         | 
| 21 27 |  | 
| 22 28 | 
             
              module ClassMethods
         | 
| 23 | 
            -
                def memoize(method_name, condition: nil)
         | 
| 29 | 
            +
                def memoize(method_name, condition: nil, ttl: nil)
         | 
| 24 30 | 
             
                  prepend_memery_module!
         | 
| 25 | 
            -
                  define_memoized_method!(method_name, condition: condition)
         | 
| 31 | 
            +
                  define_memoized_method!(method_name, condition: condition, ttl: ttl)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def memoized?(method_name)
         | 
| 35 | 
            +
                  return false unless defined?(@_memery_module)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  @_memery_module.method_defined?(method_name) ||
         | 
| 38 | 
            +
                    @_memery_module.private_method_defined?(method_name)
         | 
| 26 39 | 
             
                end
         | 
| 27 40 |  | 
| 28 41 | 
             
                private
         | 
| @@ -33,7 +46,7 @@ module Memery | |
| 33 46 | 
             
                  prepend @_memery_module
         | 
| 34 47 | 
             
                end
         | 
| 35 48 |  | 
| 36 | 
            -
                def define_memoized_method!(method_name, condition: nil)
         | 
| 49 | 
            +
                def define_memoized_method!(method_name, condition: nil, ttl: nil)
         | 
| 37 50 | 
             
                  mod_id = @_memery_module.object_id
         | 
| 38 51 | 
             
                  visibility = Memery.method_visibility(self, method_name)
         | 
| 39 52 | 
             
                  raise ArgumentError, "Method #{method_name} is not defined on #{self}" unless visibility
         | 
| @@ -46,13 +59,15 @@ module Memery | |
| 46 59 |  | 
| 47 60 | 
             
                      key = "#{method_name}_#{mod_id}"
         | 
| 48 61 |  | 
| 49 | 
            -
                      store = @_memery_memoized_values | 
| 62 | 
            +
                      store = (@_memery_memoized_values ||= {})[key] ||= {}
         | 
| 50 63 |  | 
| 51 | 
            -
                       | 
| 64 | 
            +
                      if store.key?(args) && (ttl.nil? || Memery.monotonic_clock <= store[args][:time] + ttl)
         | 
| 65 | 
            +
                        return store[args][:result]
         | 
| 66 | 
            +
                      end
         | 
| 52 67 |  | 
| 53 | 
            -
                       | 
| 54 | 
            -
                      @_memery_memoized_values[key]  | 
| 55 | 
            -
                       | 
| 68 | 
            +
                      result = super(*args)
         | 
| 69 | 
            +
                      @_memery_memoized_values[key][args] = { result: result, time: Memery.monotonic_clock }
         | 
| 70 | 
            +
                      result
         | 
| 56 71 | 
             
                    end
         | 
| 57 72 |  | 
| 58 73 | 
             
                    send(visibility, method_name)
         | 
    
        data/lib/memery/version.rb
    CHANGED
    
    
    
        data/memery.gemspec
    CHANGED
    
    | @@ -20,10 +20,13 @@ Gem::Specification.new do |spec| | |
| 20 20 | 
             
              end
         | 
| 21 21 | 
             
              spec.require_paths = ["lib"]
         | 
| 22 22 |  | 
| 23 | 
            +
              spec.add_development_dependency "benchmark-ips"
         | 
| 24 | 
            +
              spec.add_development_dependency "benchmark-memory"
         | 
| 23 25 | 
             
              spec.add_development_dependency "bundler"
         | 
| 24 26 | 
             
              spec.add_development_dependency "coveralls"
         | 
| 25 27 | 
             
              spec.add_development_dependency "pry"
         | 
| 26 28 | 
             
              spec.add_development_dependency "rake"
         | 
| 27 29 | 
             
              spec.add_development_dependency "rspec"
         | 
| 30 | 
            +
              spec.add_development_dependency "rubocop-config-umbrellio"
         | 
| 28 31 | 
             
              spec.add_development_dependency "simplecov"
         | 
| 29 32 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,43 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: memery
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Yuri Smirnov
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019- | 
| 11 | 
            +
            date: 2019-10-19 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: benchmark-ips
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - ">="
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :development
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - ">="
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: benchmark-memory
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - ">="
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '0'
         | 
| 34 | 
            +
              type: :development
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - ">="
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '0'
         | 
| 13 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 42 | 
             
              name: bundler
         | 
| 15 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -80,6 +108,20 @@ dependencies: | |
| 80 108 | 
             
                - - ">="
         | 
| 81 109 | 
             
                  - !ruby/object:Gem::Version
         | 
| 82 110 | 
             
                    version: '0'
         | 
| 111 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 112 | 
            +
              name: rubocop-config-umbrellio
         | 
| 113 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 114 | 
            +
                requirements:
         | 
| 115 | 
            +
                - - ">="
         | 
| 116 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 117 | 
            +
                    version: '0'
         | 
| 118 | 
            +
              type: :development
         | 
| 119 | 
            +
              prerelease: false
         | 
| 120 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 121 | 
            +
                requirements:
         | 
| 122 | 
            +
                - - ">="
         | 
| 123 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 124 | 
            +
                    version: '0'
         | 
| 83 125 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 84 126 | 
             
              name: simplecov
         | 
| 85 127 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -111,6 +153,7 @@ files: | |
| 111 153 | 
             
            - LICENSE.txt
         | 
| 112 154 | 
             
            - README.md
         | 
| 113 155 | 
             
            - Rakefile
         | 
| 156 | 
            +
            - benchmark.rb
         | 
| 114 157 | 
             
            - lib/memery.rb
         | 
| 115 158 | 
             
            - lib/memery/version.rb
         | 
| 116 159 | 
             
            - memery.gemspec
         |