lemo 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c30aeed79a652b56d1c5a068cd8e8497bdbf931c
4
+ data.tar.gz: e20b6ec6ff6ac8e4ccb70c1f1e08bb6279a93750
5
+ SHA512:
6
+ metadata.gz: e8dd167038ba71a0a43acd96eddf876fabc713f998335a68c366c7389326cca463864129d2ae19fb36ee2ad2cb98222a0e7b0f84db2713aa0a8dddac5334e601
7
+ data.tar.gz: 6112d081a1b85147d81cf1a351a8756f706446aa6d0cad55d7a25b3c94ca6f1a7a79da8692165337b93673e91fb98014970bfcdce1ce27459117c7f5b43f3eaa
@@ -0,0 +1,6 @@
1
+ /.bundle/
2
+ /Gemfile.lock
3
+ /coverage/
4
+ /pkg/
5
+ /spec/reports/
6
+ /tmp/
data/.rvmrc ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 2.3.1@lemo" > .rvmrc
9
+ environment_id="ruby-2.3.1@lemo"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.27.0 (master)" # 1.10.1 seems like a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | __rvm_awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ for __hook in "${rvm_path:-$HOME/.rvm}/hooks/after_use"*
27
+ do
28
+ if [[ -f "${__hook}" && -x "${__hook}" && -s "${__hook}" ]]
29
+ then \. "${__hook}" || true
30
+ fi
31
+ done
32
+ unset __hook
33
+ if (( ${rvm_use_flag:=1} >= 2 )) # display only when forced
34
+ then
35
+ if [[ $- == *i* ]] # check for interactive shells
36
+ then printf "%b" "Using: $(tput setaf 2 2>/dev/null)$GEM_HOME$(tput sgr0 2>/dev/null)\n" # show the user the ruby and gemset they are using in green
37
+ else printf "%b" "Using: $GEM_HOME\n" # don't use colors in non-interactive shells
38
+ fi
39
+ fi
40
+ else
41
+ # If the environment file has not yet been created, use the RVM CLI to select.
42
+ rvm --create "$environment_id" || {
43
+ echo "Failed to create RVM environment '${environment_id}'."
44
+ return 1
45
+ }
46
+ fi
47
+
48
+ # If you use bundler, this might be useful to you:
49
+ # if [[ -s Gemfile ]] && {
50
+ # ! builtin command -v bundle >/dev/null ||
51
+ # builtin command -v bundle | GREP_OPTIONS="" \command \grep $rvm_path/bin/bundle >/dev/null
52
+ # }
53
+ # then
54
+ # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
55
+ # gem install bundler
56
+ # fi
57
+ # if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
58
+ # then
59
+ # bundle install | GREP_OPTIONS="" \command \grep -vE '^Using|Your bundle is complete'
60
+ # fi
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1
4
+ - 2.2
5
+ - 2.3.1 # 2.3 doesn't work 09-Sep-2016
6
+ - jruby-9.0
7
+ - jruby-9.1.2.0 # 9.1 doesn't work 09-Sep-2016
8
+ before_install: gem install bundler -v 1.10.6
9
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lemo.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-2016 John Anderson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ # Lemo [![Gem Version](https://badge.fury.io/rb/lemo.png)](http://badge.fury.io/rb/lemo) [![Build Status](https://travis-ci.org/djellemah/lemo.png?branch=master)](https://travis-ci.org/djellemah/lemo)
2
+
3
+ Yet another memoize gem. Because it does some things that are important to me.
4
+
5
+ Attention paid to performance:
6
+
7
+ * rewrites the memoized method for fast normal-path execution. Rewritten method
8
+ only makes calls to ruby methods, apart from the necessary single call to the
9
+ original method.
10
+
11
+ * Uses instance variables for memoisation storage.
12
+
13
+ `Lemo::Memo` treats nil as a value, in other words uses presence or absence of
14
+ an instance variable to establish whether the value has been memoised or not.
15
+ Can clear one or several or all memoized values.
16
+
17
+ `Lemo::Ormo` behaves like `@ivar ||=` so clearing is just setting `@ivar`
18
+ to `nil`. Or removing it.
19
+
20
+ `_memoed_methods` gives a hash of methods that have been memoised, and their
21
+ unmemoised method bodies.
22
+
23
+ Works on singleton instances. Although the syntax is clunky and best avoided.
24
+
25
+ Is just as threadsafe as `||=` (in other words it mostly isn't)
26
+
27
+ Will raise an exception if you attempt to memoise methods with parameters.
28
+
29
+ ## Installation
30
+
31
+ Add this line to your application's Gemfile:
32
+
33
+ ```ruby
34
+ gem 'lemo'
35
+ ```
36
+
37
+ And then execute:
38
+
39
+ $ bundle
40
+
41
+ Or install it yourself as:
42
+
43
+ $ gem install lemo
44
+
45
+ ## Usage
46
+
47
+ ``` ruby
48
+ require 'lemo/ormo'
49
+
50
+ class YourThing
51
+ include Lemo::Ormo # or Lemo::Memo if you want nil-as-a-value and specialised clearing.
52
+
53
+ def normal_method
54
+ rand
55
+ end
56
+
57
+ memo def expensive_calculation
58
+ # do complicated stuff that is referentially transparent
59
+ end
60
+ end
61
+
62
+ your_thing = YourThing.new
63
+ your_thing.expensive_calculation
64
+ your_thing.clear_memos
65
+ ```
66
+
67
+ ## Development
68
+
69
+ `rspec` to run specs
70
+
71
+ ## Contributing
72
+
73
+ Bug reports and pull requests are welcome on GitHub at https://github.com/djellemah/lemo.
74
+
75
+ ## License
76
+
77
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lemo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "lemo"
8
+ spec.version = Lemo::VERSION
9
+ spec.authors = ["John Anderson"]
10
+ spec.email = ["panic@semiosix.com"]
11
+
12
+ spec.summary = %q{fast-ish Memoize that handles nils and singletons}
13
+ spec.description = spec.summary
14
+ spec.homepage = "http://github.com/djellemah/lemo"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "pry"
31
+ spec.add_development_dependency "bundler", "~> 1.10"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rspec"
34
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'lemo/memo'
2
+ require_relative 'lemo/ormo'
@@ -0,0 +1,55 @@
1
+ # Copyright John Anderson 2015-2016
2
+
3
+ require_relative 'memoed_methods'
4
+
5
+ module Lemo
6
+ module Memo
7
+ include MemoedMethods
8
+
9
+ module ClassMethods
10
+ def memoed_methods
11
+ @memoed_methods ||= {}
12
+ end
13
+
14
+ # provide a legal ivar name from a method name. instance variables
15
+ # can't have ? ! and other punctuation. Which isn't handled. Obviously.
16
+ def ivar_from( maybe_meth )
17
+ :"@_memo_#{maybe_meth.to_s.tr ILLEGAL_IVAR_CHARS,'pi'}"
18
+ end
19
+
20
+ def lemo( meth )
21
+ unbound_previous_method = instance_method meth
22
+
23
+ # still doesn't prevent memoisation of methods with an implicit block
24
+ unless unbound_previous_method.parameters.empty?
25
+ raise ArgumentError, "can't memo #{meth} with parameters"
26
+ end
27
+
28
+ memoed_methods[meth] = unbound_previous_method
29
+ ivar = ivar_from meth
30
+
31
+ define_method meth do
32
+ # This gets executed on every call to meth, so make it fast.
33
+ if instance_variable_defined? ivar
34
+ instance_variable_get ivar
35
+ else
36
+ # bind the saved method to this instance, call the result ...
37
+ to_memo = unbound_previous_method.bind( self ).call
38
+ # ... memo it and return value
39
+ instance_variable_set ivar, to_memo
40
+ end
41
+ end
42
+
43
+ meth
44
+ end
45
+
46
+ # for all the things using memo already
47
+ alias memo lemo
48
+ end
49
+
50
+ # hook in class methods on include
51
+ def self.included( other_module )
52
+ other_module.extend ClassMethods
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,44 @@
1
+ module Lemo
2
+ ILLEGAL_IVAR_CHARS = '?!'.freeze
3
+
4
+ module MemoedMethods
5
+ # the set of methods memoed so far that we know about.
6
+ def _memoed_methods
7
+ methods = {}
8
+
9
+ if self.class.respond_to?(:memoed_methods)
10
+ methods.merge! self.class.memoed_methods
11
+ end
12
+
13
+ if singleton_methods.size > 0 && singleton_class.respond_to?(:memoed_methods)
14
+ methods.merge! singleton_class.memoed_methods
15
+ end
16
+
17
+ methods
18
+ end
19
+
20
+ # Reset some or all memoized variables.
21
+ # Return cleared value(s)
22
+ # Has to do quite a lot of meta-work, so don't put this in fast-path code.
23
+ def _clear_memos( *requested_meths )
24
+ # construct set of memoed methods to clear
25
+ requested_meths =
26
+ if requested_meths.empty?
27
+ _memoed_methods.keys
28
+ else
29
+ # only clear ivars that actually make sense
30
+ _memoed_methods.keys & requested_meths
31
+ end
32
+
33
+ # clear set of memos and keep their values
34
+ memoed_values = requested_meths.map do |meth|
35
+ if instance_variable_defined?( ivar = _memoed_methods[meth].owner.ivar_from(meth) )
36
+ remove_instance_variable(ivar)
37
+ end
38
+ end
39
+
40
+ # return nil, the first value, or all values
41
+ (0..1) === memoed_values.size ? memoed_values.first : memoed_values
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ # Copyright John Anderson 2015-2016
2
+
3
+ require_relative 'memoed_methods'
4
+
5
+ module Lemo
6
+ # simple replacement for ||= (y'know or-or-equals)
7
+ # in others words stores values as plain old @name, and nil means same as does-not-exist
8
+ module Ormo
9
+ include MemoedMethods
10
+
11
+ module ClassMethods
12
+ def memoed_methods
13
+ @memoed_methods ||= {}
14
+ end
15
+
16
+ # provide a legal ivar name from a method name. instance variables
17
+ # can't have ? ! and other punctuation. Which isn't handled. Obviously.
18
+ # WARNING meth, meth? and meth! will access the same ivar.
19
+ def ivar_from( meth )
20
+ :"@#{meth.to_s.delete ILLEGAL_IVAR_CHARS}"
21
+ end
22
+
23
+ # WARNING race condition if two threads concurrently define the same
24
+ # memo'ed method on the same class. Unlikely, but still.
25
+ def ormo( meth )
26
+ unbound_previous_method = instance_method meth
27
+
28
+ # still doesn't prevent memoisation of methods with an implicit block
29
+ unless unbound_previous_method.parameters.empty?
30
+ raise ArgumentError, "can't memo #{meth} with parameters"
31
+ end
32
+
33
+ # keep this for initial calculation, and recalculation
34
+ memoed_methods[meth] = unbound_previous_method
35
+ ivar = ivar_from meth
36
+
37
+ # Define the class using instance variable @ syntax, for fastest
38
+ # runtime. Use class_eval to define an instance method, cos self is the
39
+ # class (or singleton class) right now
40
+ class_eval <<-RUBY, __FILE__, __LINE__
41
+ def #{meth}
42
+ #{ivar} ||= _memoed_methods[:#{meth}].bind(self).call
43
+ end
44
+ RUBY
45
+
46
+ # allow chaining of symbol returned from def
47
+ meth
48
+ end
49
+
50
+ # for all the things using memo already
51
+ alias memo ormo
52
+ end
53
+
54
+ # hook in class methods on include
55
+ def self.included( other_module )
56
+ other_module.extend ClassMethods
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module Lemo
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lemo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - John Anderson
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
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: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: fast-ish Memoize that handles nils and singletons
70
+ email:
71
+ - panic@semiosix.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rvmrc"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lemo.gemspec
84
+ - lib/lemo.rb
85
+ - lib/lemo/memo.rb
86
+ - lib/lemo/memoed_methods.rb
87
+ - lib/lemo/ormo.rb
88
+ - lib/lemo/version.rb
89
+ homepage: http://github.com/djellemah/lemo
90
+ licenses:
91
+ - MIT
92
+ metadata:
93
+ allowed_push_host: https://rubygems.org
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.5.1
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: fast-ish Memoize that handles nils and singletons
114
+ test_files: []