method_found 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|