modifiers 0.0.2 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: faba910e56e4a55c7099c4b1ff3220fba30e9128
4
- data.tar.gz: 2cdc9f5a50ab604b3696dcdc76169100c0bfdfd8
3
+ metadata.gz: 1362dad9adfb0a9ac28f10588f0f233f00472038
4
+ data.tar.gz: 22c39749a7f5ae6c411754899ae57db54fd7e52a
5
5
  SHA512:
6
- metadata.gz: de75437da106da90335181f2ac230497be4b6a1e4a58fe7223cb232d0ba7fcdabaf13f2455d6e09b51f9c12e0f574af203eac97e3b8483a458a2af3d8bdc1a35
7
- data.tar.gz: 44fb0c587838f51a93ca018c5bb2baf3bb5750545310ba7c1ef29e1a8a346802b1a0ee56e4a131bbfdb2927956ccbad1b0dc1c90f1b84cd7fe39eb4a123b6a8c
6
+ metadata.gz: 3bebfb125f3104c010eb94e4ee1b9de4c40cdeec0f9f542c45d5cb074631a4b3ef93f86ba7b07d6391d16387fb6db8d479144d0ba37e5baa671cabe483b6c9f0
7
+ data.tar.gz: 27007ced57e661678ceba35749a1cdad47c764375cafeab094482626677a8a547cda788209a145fc21ae12449161cf520ec78f7da2d05f9a0b76d359f08666b3
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --order rand
@@ -1,5 +1,13 @@
1
- nguage: ruby
1
+ language: ruby
2
2
  rvm:
3
+ - 2.0.0
3
4
  - 2.1.0
4
5
  - 2.1.1
5
6
  - 2.1.2
7
+ deploy:
8
+ provider: rubygems
9
+ api_key:
10
+ secure: fewAgjCXLYSkbRq57EQYRTjwPn/LfQgpvSh1HBdVG1Vjds2OCL+yl3d6x30o4q28GU7dMLKjEl2BZrrQ9TwPgit3LRPUwlsoZRvGQj+oztdNfshpWansne6coBYa5vx/tQFBSDWkTMdE2RMH1w7qP20Iqn44c4yQU1tQiqWoZMQ=
11
+ on:
12
+ tags: true
13
+ repo: nicknovitski/modifiers
data/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ # Modifiers
2
+ [![Build Status](https://travis-ci.org/nicknovitski/modifiers.svg?branch=master)](https://travis-ci.org/nicknovitski/modifiers)
3
+
1
4
  ## What is/are Modifiers?
2
5
 
3
6
  `Modifiers` is a collection of method modifiers, and a way to make more.
@@ -122,7 +125,7 @@ class DuckFarmer < Struct.new(:hutches)
122
125
  extend Modifiers
123
126
 
124
127
  query def fullest_hutch
125
- hutches.max { |h1,h2| h1.count_eggs - h2.count_eggs
128
+ hutches.max { |h1,h2| h1.count_eggs - h2.count_eggs }
126
129
  end
127
130
  end
128
131
 
@@ -1,8 +1,8 @@
1
1
  require 'modifiers/define_modifier'
2
2
 
3
3
  module Modifiers
4
- define_modifier(:command) do |invocation|
5
- invocation.invoke
4
+ define_modifier(:command) do |*args, &block|
5
+ super(*args, &block)
6
6
  nil
7
7
  end
8
8
  end
@@ -1,11 +1,11 @@
1
1
  require 'modifiers/modification'
2
2
 
3
3
  module Modifiers
4
- def define_modifier(name, &block)
5
- define_method(name) do |sym|
6
- mod = Modification.new(klass: self, method: sym)
7
- define_method(sym) { |*args| mod.call(self, *args, &block) }
8
- send(mod.original_visibility, sym)
4
+ def define_modifier(modifier, helper = nil, &method_body)
5
+ define_method(modifier) do |modified|
6
+ mod = Modification.new(modifier, self, modified, method_body)
7
+ mod.send(:include, helper) if helper
8
+ prepend mod
9
9
  end
10
10
  end
11
11
  module_function :define_modifier
@@ -1,8 +1,36 @@
1
1
  require 'modifiers/define_modifier'
2
2
 
3
3
  module Modifiers
4
- define_modifier(:deprecated) do |invocation|
5
- warn "deprecated method #{invocation.method_identifier} called from #{invocation.location.join(':')}"
6
- invocation.invoke
4
+ module Deprecated
5
+ private
6
+
7
+ def kaller
8
+ caller[4]
9
+ end
10
+
11
+ def file
12
+ match_caller(/(.*?):/i)
13
+ end
14
+
15
+ def line
16
+ match_caller(/:(\d+).*?$/i)
17
+ end
18
+
19
+ def match_caller(pattern)
20
+ kaller.match(pattern)[1].to_s
21
+ end
22
+
23
+ def location
24
+ [file, line].join(':')
25
+ end
26
+
27
+ def target
28
+ is_a?(Module) ? "#{self}." : "#{self.class}#"
29
+ end
30
+ end
31
+
32
+ define_modifier(:deprecated, Deprecated) do |*args, &block|
33
+ warn "deprecated method #{target}#{__method__} called from #{location}"
34
+ super(*args, &block)
7
35
  end
8
36
  end
@@ -1,12 +1,28 @@
1
1
  require 'modifiers/define_modifier'
2
2
 
3
3
  module Modifiers
4
- define_modifier(:memoized) do |invocation|
5
- ivar = "@#{invocation.method_name}".to_sym
4
+ module Memoized
5
+ private
6
6
 
7
- instance_variable_set(ivar, {}) unless instance_variable_defined?(ivar)
7
+ def ivar(method_name)
8
+ "@#{method_name}".to_sym
9
+ end
8
10
 
9
- memoizer = instance_variable_get(ivar)
10
- memoizer.fetch(invocation.arguments) { memoizer[invocation.arguments] = invocation.invoke }
11
+ def init_memo(method_name)
12
+ instance_variable_set(ivar(method_name), {}) unless instance_variable_defined?(ivar(method_name))
13
+ end
14
+
15
+ def memoizer_for(method_name)
16
+ instance_variable_get(ivar(method_name))
17
+ end
18
+
19
+ def memoizer_fetch(method_name, key, &block)
20
+ memoizer_for(method_name).fetch(key, &block)
21
+ end
22
+ end
23
+
24
+ define_modifier(:memoized, Memoized) do |*args, &block|
25
+ init_memo(__method__)
26
+ memoizer_fetch(__method__, args) { memoizer_for(__method__)[args] = super(*args, &block) }
11
27
  end
12
28
  end
@@ -1,42 +1,28 @@
1
- require 'modifiers/method_invocation'
2
-
3
- module Modifiers
4
- class Modification
5
- def initialize(klass:, method:)
6
- @klass = klass
7
- @original_method = klass.send(:instance_method, method)
8
- @original_visibility = visibility_on(klass)
9
- end
10
-
11
- attr_reader :original_visibility
12
-
13
- def call(instance, *args, &block)
14
- invocation = MethodInvocation.new(method: original_method, receiver: instance, arguments: args)
15
- if block
16
- instance.instance_exec(invocation, &block)
17
- else
18
- invocation.invoke
19
- end
1
+ class Modification < Module
2
+ def initialize(name, receiver, method_name, method_body)
3
+ @name, @receiver, @method_name = name, receiver, method_name
4
+ super() do
5
+ define_method(method_name, &method_body)
6
+ set_visibility
20
7
  end
8
+ end
21
9
 
22
- private
10
+ private
23
11
 
24
- attr_reader :original_method, :klass
12
+ attr_reader :receiver, :method_name
25
13
 
26
- def visibility_on(klass)
27
- if klass.private_method_defined?(original_method.name)
28
- :private
29
- elsif klass.protected_method_defined?(original_method.name)
30
- :protected
31
- else
32
- :public
33
- end
14
+ def set_visibility
15
+ case
16
+ when private? then private(method_name)
17
+ when protected? then protected(method_name)
34
18
  end
19
+ end
35
20
 
36
- def instance
37
- klass.new
38
- rescue TypeError # klass is a singleton metaclass
39
- ObjectSpace.each_object(klass).first
40
- end
21
+ def protected?
22
+ receiver.protected_method_defined?(method_name)
23
+ end
24
+
25
+ def private?
26
+ receiver.private_method_defined?(method_name)
41
27
  end
42
28
  end
@@ -1,7 +1,30 @@
1
1
  require 'modifiers/define_modifier'
2
2
 
3
3
  module Modifiers
4
- define_modifier(:query) do |invocation|
5
- invocation.invoke(Marshal.load(Marshal.dump(self)))
4
+ module Querying
5
+ def ivars_hash
6
+ instance_variables.each_with_object({}) do |ivar, hash|
7
+ hash[ivar] = instance_variable_get(ivar)
8
+ end
9
+ end
10
+
11
+ def duplicate_ivars!
12
+ ivars_hash.each { |k,v| instance_variable_set(k, Marshal.load(Marshal.dump(v))) }
13
+ end
14
+
15
+ def set_ivars(hash)
16
+ hash.each { |k, v| instance_variable_set(k, v) }
17
+
18
+ in_state_but_not_hash = instance_variables.select { |i| hash[i].nil? }
19
+ in_state_but_not_hash.each { |i| remove_instance_variable(i) }
20
+ end
21
+ end
22
+
23
+ define_modifier(:query, Querying) do |*args, &block|
24
+ orig_ivars = ivars_hash
25
+ duplicate_ivars!
26
+ return_value = super(*args, &block)
27
+ set_ivars(orig_ivars)
28
+ return_value
6
29
  end
7
30
  end
@@ -1,3 +1,3 @@
1
1
  module Modifiers
2
- VERSION = '0.0.2'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(/^spec\//)
18
18
  spec.require_paths = ['lib']
19
19
 
20
- spec.required_ruby_version = '>= 2.1.0'
20
+ spec.required_ruby_version = '>= 2.0.0'
21
21
 
22
22
  spec.add_development_dependency 'bundler', '~> 1.6'
23
23
  spec.add_development_dependency 'rake'
@@ -4,10 +4,11 @@ require 'modifiers/command'
4
4
  RSpec.describe Modifiers do
5
5
  class Doer
6
6
  extend Modifiers
7
- command def take_action
7
+ def take_action
8
8
  # lots of side-effectful-operations
9
9
  2 + 2
10
10
  end
11
+ command :take_action
11
12
  end
12
13
 
13
14
  subject(:doer) { Doer.new }
@@ -3,21 +3,45 @@ require 'modifiers/define_modifier'
3
3
 
4
4
  module MyModifier
5
5
  extend Modifiers
6
- define_modifier(:bro) { |i| i.invoke + ', bro' }
6
+ define_modifier(:bro) do |*args, &block|
7
+ super(*args, &block) + ', bro'
8
+ end
9
+
10
+ module Watch
11
+ private
12
+
13
+ def timestamp
14
+ Time.now
15
+ end
16
+ end
17
+
18
+ define_modifier(:timed, Watch) do |*args, &block|
19
+ super(*args, &block) + "(#{__method__} called at #{timestamp})"
20
+ end
7
21
  end
8
22
 
9
23
  class Foo
10
24
  extend MyModifier
11
25
 
12
- bro def hello(name = 'world')
26
+ def hello(name = 'world')
13
27
  "hello, #{name}"
14
28
  end
29
+ bro :hello
30
+
31
+ def poot
32
+ "I accidentally typed the name of this method but I think it's great"
33
+ end
34
+ timed :poot
15
35
  end
16
36
 
17
37
  RSpec.describe Modifiers do
18
38
  describe '.define_modifier' do
19
39
  it 'can make new modifiers in new modules' do
20
- expect(Foo.new.hello).to eq('hello, world, bro')
40
+ expect(Foo.new.hello('modified')).to eq('hello, modified, bro')
41
+ end
42
+
43
+ it 'can include helper modules' do
44
+ expect(Foo.new.poot).to match(/\(poot called at/)
21
45
  end
22
46
  end
23
47
  end
@@ -3,41 +3,47 @@ require 'modifiers/deprecated'
3
3
 
4
4
  RSpec.describe Modifiers do
5
5
  describe '#deprecated' do
6
- it_behaves_like 'a modifier', :deprecated
6
+ context "with silenced warnings" do
7
+ around(:example) do |example|
8
+ old_verbose, $VERBOSE = $VERBOSE, nil
9
+ example.call
10
+ $VERBOSE = old_verbose
11
+ end
7
12
 
8
- let(:test_class) do
9
- class Test
10
- extend Modifiers
13
+ it_behaves_like 'a modifier', :deprecated
14
+ end
11
15
 
12
- deprecated def method
13
- # something you shouldn't be using
14
- end
16
+ class Test
17
+ extend Modifiers
18
+
19
+ def method
20
+ # something you shouldn't be using
15
21
  end
22
+ deprecated :method
16
23
  end
17
- subject(:test_instance) { test_class.new }
18
24
 
19
- around(:example) do |example|
20
- old_verbose, $VERBOSE = $VERBOSE, nil
21
- example.call
22
- $VERBOSE = old_verbose
25
+ subject(:test_instance) do
26
+ s = Test.new
27
+ allow(s).to receive(:warn)
28
+ s
23
29
  end
24
30
 
25
31
  it 'changes the method to warn it has been deprecated' do
26
- expect(subject).to receive(:warn).with(/deprecated/)
27
-
28
32
  subject.method
33
+
34
+ expect(subject).to have_received(:warn).with(/deprecated/)
29
35
  end
30
36
 
31
37
  it 'includes the name of the method in the warning' do
32
- expect(subject).to receive(:warn).with(/Test\#method/)
33
-
34
38
  subject.method
39
+
40
+ expect(subject).to have_received(:warn).with(/Test\#method/)
35
41
  end
36
42
 
37
43
  it 'includes the location of the call in the warning' do
38
- expect(subject).to receive(:warn).with(/deprecated_spec.rb/)
39
-
40
44
  subject.method
45
+
46
+ expect(subject).to have_received(:warn).with(/deprecated_spec.rb/)
41
47
  end
42
48
 
43
49
  context 'class methods' do
@@ -45,17 +51,18 @@ RSpec.describe Modifiers do
45
51
  class Test
46
52
  class << self
47
53
  extend Modifiers
48
- deprecated def class_method
54
+ def class_method
49
55
  #
50
56
  end
57
+ deprecated :class_method
51
58
  end
52
59
  end
53
60
  end
54
61
 
55
62
  it 'names the method correctly in the warning' do
56
- expect(test_class).to receive(:warn).with(/Test\.class_method/)
63
+ expect(Test).to receive(:warn).with(/Test\.class_method/)
57
64
 
58
- test_class.class_method
65
+ Test.class_method
59
66
  end
60
67
  end
61
68
  end
@@ -11,9 +11,10 @@ RSpec.describe Modifiers do
11
11
  class Client
12
12
  extend Modifiers
13
13
 
14
- memoized def call_service
14
+ def call_service
15
15
  Service.expensive_operation
16
16
  end
17
+ memoized :call_service
17
18
  end
18
19
 
19
20
  subject(:client) { Client.new }
@@ -12,22 +12,28 @@ RSpec.describe Modifiers do
12
12
 
13
13
  attr_reader :memo, :enum
14
14
 
15
- query def fooer?
15
+ def fooer?
16
16
  @memo ||= true
17
17
  end
18
+ query :fooer?
18
19
 
19
- query def insertable?(val)
20
+ def insertable?(val)
20
21
  @enum << val
21
22
  end
23
+ query :insertable?
22
24
 
23
- query def child_status
25
+ def child_status
24
26
  @sub_test.status
25
27
  end
28
+ query :child_status
26
29
  end
27
30
 
28
31
  class SubTest
32
+ def initialize
33
+ @state_changes = []
34
+ end
29
35
  def status
30
- @status ||= :awake
36
+ @state_changes << :awake
31
37
  end
32
38
  end
33
39
 
@@ -46,7 +52,7 @@ RSpec.describe Modifiers do
46
52
  end
47
53
 
48
54
  it 'prevents the method from altering linked objects' do
49
- expect { instance.child_status }.not_to change { child.instance_variables }
55
+ expect { instance.child_status }.not_to change { child.instance_variable_get(:@state_changes) }
50
56
  end
51
57
  end
52
58
  end
metadata CHANGED
@@ -1,55 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modifiers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Novitski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-19 00:00:00.000000000 Z
11
+ date: 2014-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.6'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  description: A simple and composable way to add functionality to methods.
@@ -59,8 +59,9 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
- - ".gitignore"
63
- - ".travis.yml"
62
+ - .gitignore
63
+ - .rspec
64
+ - .travis.yml
64
65
  - Gemfile
65
66
  - LICENSE.txt
66
67
  - README.md
@@ -71,7 +72,6 @@ files:
71
72
  - lib/modifiers/define_modifier.rb
72
73
  - lib/modifiers/deprecated.rb
73
74
  - lib/modifiers/memoized.rb
74
- - lib/modifiers/method_invocation.rb
75
75
  - lib/modifiers/modification.rb
76
76
  - lib/modifiers/query.rb
77
77
  - lib/modifiers/version.rb
@@ -93,17 +93,17 @@ require_paths:
93
93
  - lib
94
94
  required_ruby_version: !ruby/object:Gem::Requirement
95
95
  requirements:
96
- - - ">="
96
+ - - '>='
97
97
  - !ruby/object:Gem::Version
98
- version: 2.1.0
98
+ version: 2.0.0
99
99
  required_rubygems_version: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - '>='
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  requirements: []
105
105
  rubyforge_project:
106
- rubygems_version: 2.2.0
106
+ rubygems_version: 2.0.14
107
107
  signing_key:
108
108
  specification_version: 4
109
109
  summary: Cute and Easy method modifiers (also called decorators)
@@ -1,51 +0,0 @@
1
- module Modifiers
2
- class MethodInvocation
3
- def initialize(method:, receiver:, arguments:)
4
- @method = method
5
- @receiver = receiver
6
- @arguments = arguments
7
- end
8
-
9
- attr_reader :arguments
10
-
11
- def invoke(context = receiver)
12
- method.bind(context).call(*arguments)
13
- end
14
-
15
- def location
16
- [file, line_no]
17
- end
18
-
19
- def method_name
20
- method.name
21
- end
22
-
23
- def method_identifier
24
- target + method_name.to_s
25
- end
26
-
27
- private
28
-
29
- def target
30
- receiver.is_a?(Module) ? "#{receiver}." : "#{receiver.class}#"
31
- end
32
-
33
- def file
34
- match_caller(/(.*?):/i)
35
- end
36
-
37
- def line_no
38
- match_caller(/:(\d+).*?$/i)
39
- end
40
-
41
- def match_caller(regex)
42
- method_caller.match(regex).to_s
43
- end
44
-
45
- def method_caller
46
- caller[7]
47
- end
48
-
49
- attr_reader :method, :receiver
50
- end
51
- end