method_found 0.1.3 → 0.1.4
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/README.md +1 -1
- data/lib/method_found/attribute_interceptor.rb +73 -0
- data/lib/method_found/attribute_methods.rb +69 -0
- data/lib/method_found/interceptor.rb +15 -7
- data/lib/method_found/version.rb +1 -1
- metadata +4 -3
- data/lib/method_found/simple_model.rb +0 -109
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bc50110082afd5058e16422c320e7b48ca7ea75
|
4
|
+
data.tar.gz: 154aa54df987957c7b405ac9f669546a69790f33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ccf3e88a3726c8c709b0df1c479952d85f08e787e2d1cf218eeef2a59f1f62128dbc27e534fb13c214b55f0c0c7644b418ec66d7502247316fe209c11f544b0
|
7
|
+
data.tar.gz: 2bb0be7f5e689e5aec743de2dc90d5bbb0bcf534ff76207afede9373eac35ae52073fcd63951859d4e656f7c227f646dc98fc12837fdf6689188db1311f035ba
|
data/README.md
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
require "method_found"
|
2
|
+
|
3
|
+
module MethodFound
|
4
|
+
=begin
|
5
|
+
|
6
|
+
Class defining prefix, suffix and affix methods to a class for a given
|
7
|
+
attribute name or set of attribute names.
|
8
|
+
|
9
|
+
@example
|
10
|
+
class Person < Struct.new(:attributes)
|
11
|
+
include MethodFound::AttributeInterceptor.new
|
12
|
+
include MethodFound::AttributeInterceptor.new(suffix: '=')
|
13
|
+
include MethodFound::AttributeInterceptor.new(suffix: '_contrived?')
|
14
|
+
include MethodFound::AttributeInterceptor.new(prefix: 'clear_')
|
15
|
+
include MethodFound::AttributeInterceptor.new(prefix: 'reset_', suffix: '_to_default!')
|
16
|
+
|
17
|
+
def initialize(attributes = {})
|
18
|
+
super(attributes)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def attribute_contrived?(attr)
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def clear_attribute(attr)
|
28
|
+
send("#{attr}=", nil)
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset_attribute_to_default!(attr)
|
32
|
+
send("#{attr}=", 'Default Name')
|
33
|
+
end
|
34
|
+
|
35
|
+
def attribute(attr)
|
36
|
+
attributes[attr]
|
37
|
+
end
|
38
|
+
|
39
|
+
def attribute=(attr, value)
|
40
|
+
attributes[attr] = value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
=end
|
44
|
+
class AttributeInterceptor < Interceptor
|
45
|
+
def initialize(prefix: '', suffix: '')
|
46
|
+
@prefix, @suffix = prefix, suffix
|
47
|
+
regex_ = regex
|
48
|
+
attribute_matcher = proc do |method_name|
|
49
|
+
(matches = regex_.match(method_name)) && attributes.include?(matches[1]) && matches[1]
|
50
|
+
end
|
51
|
+
attribute_matcher.define_singleton_method :inspect do
|
52
|
+
regex_.inspect
|
53
|
+
end
|
54
|
+
|
55
|
+
super attribute_matcher do |_, attr_name, *arguments, &block|
|
56
|
+
send("#{prefix}attribute#{suffix}", attr_name, *arguments, &block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def regex
|
61
|
+
/\A(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})\z/.freeze
|
62
|
+
end
|
63
|
+
|
64
|
+
def define_attribute_methods(*attr_names)
|
65
|
+
prefix, suffix = @prefix, @suffix
|
66
|
+
attr_names.each do |attr_name|
|
67
|
+
define_method "#{@prefix}#{attr_name}#{@suffix}".freeze do |*arguments, &block|
|
68
|
+
send("#{prefix}attribute#{suffix}".freeze, attr_name, *arguments, &block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "method_found/attribute_interceptor"
|
2
|
+
|
3
|
+
module MethodFound
|
4
|
+
=begin
|
5
|
+
|
6
|
+
@example
|
7
|
+
class Person < Struct.new(:attributes)
|
8
|
+
include MethodFound::AttributeMethods
|
9
|
+
|
10
|
+
attribute_method_suffix ''
|
11
|
+
attribute_method_suffix '='
|
12
|
+
attribute_method_suffix '_contrived?'
|
13
|
+
attribute_method_prefix 'clear_'
|
14
|
+
attribute_method_affix prefix: 'reset_', suffix: '_to_default!'
|
15
|
+
|
16
|
+
define_attribute_methods :name
|
17
|
+
|
18
|
+
def initialize(attributes = {})
|
19
|
+
super(attributes)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def attribute_contrived?(attr)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def clear_attribute(attr)
|
29
|
+
send("#{attr}=", nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset_attribute_to_default!(attr)
|
33
|
+
send("#{attr}=", 'Default Name')
|
34
|
+
end
|
35
|
+
|
36
|
+
def attribute(attr)
|
37
|
+
attributes[attr]
|
38
|
+
end
|
39
|
+
|
40
|
+
def attribute=(attr, value)
|
41
|
+
attributes[attr] = value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
=end
|
46
|
+
module AttributeMethods
|
47
|
+
def self.included(base)
|
48
|
+
base.instance_eval do
|
49
|
+
def attribute_method_affix(prefix: '', suffix: '')
|
50
|
+
include(AttributeInterceptor.new(prefix: prefix, suffix: suffix))
|
51
|
+
end
|
52
|
+
|
53
|
+
def attribute_method_suffix(suffix)
|
54
|
+
include(AttributeInterceptor.new(suffix: suffix))
|
55
|
+
end
|
56
|
+
|
57
|
+
def attribute_method_prefix(prefix)
|
58
|
+
include(AttributeInterceptor.new(prefix: prefix))
|
59
|
+
end
|
60
|
+
|
61
|
+
def define_attribute_methods(*attributes)
|
62
|
+
ancestors.each do |ancestor|
|
63
|
+
ancestor.define_attribute_methods(*attributes) if ancestor.is_a?(AttributeInterceptor)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -27,7 +27,7 @@ string/symbol.
|
|
27
27
|
method_cacher = method(:cache_method)
|
28
28
|
|
29
29
|
define_method :method_missing do |method_name, *arguments, &method_block|
|
30
|
-
if matches = matcher_.match(method_name)
|
30
|
+
if matches = matcher_.match(method_name, self)
|
31
31
|
method_cacher.(method_name, matches)
|
32
32
|
send(method_name, *arguments, &method_block)
|
33
33
|
else
|
@@ -36,7 +36,7 @@ string/symbol.
|
|
36
36
|
end
|
37
37
|
|
38
38
|
define_method :respond_to_missing? do |method_name, include_private = false|
|
39
|
-
if matches = matcher_.match(method_name)
|
39
|
+
if matches = matcher_.match(method_name, self)
|
40
40
|
method_cacher.(method_name, matches)
|
41
41
|
else
|
42
42
|
super(method_name, include_private)
|
@@ -53,9 +53,13 @@ string/symbol.
|
|
53
53
|
private
|
54
54
|
|
55
55
|
def cache_method(method_name, matches)
|
56
|
-
intercept_method = @intercept_method
|
56
|
+
intercept_method, matcher = @intercept_method, @matcher
|
57
57
|
define_method method_name do |*arguments, &block|
|
58
|
-
|
58
|
+
if matcher.proc? && !(matches = matcher.match(method_name, self))
|
59
|
+
return super(*arguments, &block)
|
60
|
+
end
|
61
|
+
arguments = [matches, *arguments] unless method(intercept_method).arity == 1
|
62
|
+
send(intercept_method, method_name, *arguments, &block)
|
59
63
|
end
|
60
64
|
end
|
61
65
|
|
@@ -66,16 +70,20 @@ string/symbol.
|
|
66
70
|
end
|
67
71
|
|
68
72
|
class Matcher < Struct.new(:matcher)
|
69
|
-
def match(method_name)
|
73
|
+
def match(method_name, context)
|
70
74
|
if matcher.is_a?(Regexp)
|
71
75
|
matcher.match(method_name)
|
72
76
|
elsif matcher.respond_to?(:call)
|
73
|
-
|
77
|
+
context.instance_exec(method_name, &matcher)
|
74
78
|
else
|
75
|
-
(matcher.to_sym == method_name)
|
79
|
+
(matcher.to_sym == method_name)
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
83
|
+
def proc?
|
84
|
+
matcher.is_a?(Proc)
|
85
|
+
end
|
86
|
+
|
79
87
|
def inspect
|
80
88
|
matcher.inspect
|
81
89
|
end
|
data/lib/method_found/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: method_found
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Salzberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -71,9 +71,10 @@ files:
|
|
71
71
|
- bin/console
|
72
72
|
- bin/setup
|
73
73
|
- lib/method_found.rb
|
74
|
+
- lib/method_found/attribute_interceptor.rb
|
75
|
+
- lib/method_found/attribute_methods.rb
|
74
76
|
- lib/method_found/builder.rb
|
75
77
|
- lib/method_found/interceptor.rb
|
76
|
-
- lib/method_found/simple_model.rb
|
77
78
|
- lib/method_found/version.rb
|
78
79
|
- method_found.gemspec
|
79
80
|
homepage: https://github.com/shioyama/method_found
|
@@ -1,109 +0,0 @@
|
|
1
|
-
require "method_found"
|
2
|
-
|
3
|
-
module MethodFound
|
4
|
-
=begin
|
5
|
-
|
6
|
-
Example class using MethodFound interceptors to implement attributes and change
|
7
|
-
tracking. Symbols passed into constructor define patterns in interceptor
|
8
|
-
modules to catch method calls and define methods on module as needed. New
|
9
|
-
methods can be dynamically defined by calling any undefined method with a bang (!).
|
10
|
-
|
11
|
-
This class is not included by default when requiring MethodFound, so you will need to explicitly require it with:
|
12
|
-
|
13
|
-
require "method_found/simple_model"
|
14
|
-
|
15
|
-
@example Setting Attributes
|
16
|
-
post = MethodFound::SimpleModel.new(:title, :content)
|
17
|
-
|
18
|
-
post.title = "Method Found"
|
19
|
-
post.content = "Once upon a time..."
|
20
|
-
|
21
|
-
post.title
|
22
|
-
#=> "Method Found"
|
23
|
-
|
24
|
-
post.content
|
25
|
-
#=> "Once upon a time..."
|
26
|
-
|
27
|
-
@example Dyanamically defining new attributes
|
28
|
-
post = MethodFound::SimpleModel.new
|
29
|
-
|
30
|
-
post.foo
|
31
|
-
#=> raises MethodNotFound error
|
32
|
-
|
33
|
-
post.foo!
|
34
|
-
#=> nil
|
35
|
-
|
36
|
-
post.foo = "my foo"
|
37
|
-
post.foo
|
38
|
-
#=> "my foo"
|
39
|
-
|
40
|
-
@example Tracking changes
|
41
|
-
post = MethodFound::SimpleModel.new(:title)
|
42
|
-
|
43
|
-
post.title = "foo"
|
44
|
-
post.title_changed?
|
45
|
-
#=> false
|
46
|
-
|
47
|
-
post.title = "bar"
|
48
|
-
post.title_changed?
|
49
|
-
#=> true
|
50
|
-
post.title_was
|
51
|
-
#=> "foo"
|
52
|
-
post.title_changes
|
53
|
-
#=> ["bar", "foo"]
|
54
|
-
|
55
|
-
=end
|
56
|
-
class SimpleModel
|
57
|
-
attr_reader :attribute_builder
|
58
|
-
|
59
|
-
# @param attribute_names [Symbol] One or more attribute names to define
|
60
|
-
# interceptors for on model.
|
61
|
-
def initialize(*attribute_names)
|
62
|
-
@attributes = Hash.new { |h, k| h[k] = [] }
|
63
|
-
|
64
|
-
@attribute_builder = builder = Builder.new do
|
65
|
-
def define_missing(attribute_name)
|
66
|
-
intercept /\A(#{attribute_name})(=|\?)?\Z/.freeze do |_, matches, *arguments|
|
67
|
-
name = matches[1]
|
68
|
-
value = @attributes[name].last
|
69
|
-
|
70
|
-
if matches[2] == "=".freeze
|
71
|
-
new_value = arguments[0]
|
72
|
-
@attributes[name].push(new_value) unless new_value == value
|
73
|
-
else
|
74
|
-
matches[2] == "?".freeze ? !!value : value
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
intercept /\A(reset_(#{attribute_name})|(#{attribute_name})_(changed\?|changes|was))\Z/.freeze do |_, matches|
|
79
|
-
if matches[1] == "reset_#{attribute_name}".freeze
|
80
|
-
!!@attributes.delete(matches[2])
|
81
|
-
else
|
82
|
-
changes = @attributes[matches[3]]
|
83
|
-
if matches[4] == "changes".freeze
|
84
|
-
changes.reverse
|
85
|
-
elsif matches[4] == "was".freeze
|
86
|
-
changes[-2]
|
87
|
-
else
|
88
|
-
changes.size > 1
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
intercept /\A(.+)\!\Z/ do |_, matches|
|
95
|
-
builder.define_missing matches[1]
|
96
|
-
singleton_class.include builder
|
97
|
-
@attributes[matches[1]].last
|
98
|
-
end
|
99
|
-
end
|
100
|
-
attribute_names.each { |name| builder.define_missing(name) }
|
101
|
-
|
102
|
-
singleton_class.include builder
|
103
|
-
end
|
104
|
-
|
105
|
-
def attributes
|
106
|
-
Hash[@attributes.map { |k, v| [k, v.last] }]
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|