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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c80b59b4cfa2a683e9eaf250f975cebeff2594f0
4
- data.tar.gz: 7004c79a99fddd803d098f4ebabe5541d8ebfeae
3
+ metadata.gz: 9bc50110082afd5058e16422c320e7b48ca7ea75
4
+ data.tar.gz: 154aa54df987957c7b405ac9f669546a69790f33
5
5
  SHA512:
6
- metadata.gz: ccb77ec85e8d6b98b66315c53853088308ba74a760dabe638499717f597ccdf9c2dcf44378cb1c4c1a26cf35202963fc8116c6154d785581f7a43b7dee4e96f6
7
- data.tar.gz: b76fbe65a0a186bb5f361ab251c9d514e20b020aa0ac0949dfacfcf832e8a997392c519c23e85615227599ec0ed97feeedf5facdd1372792692fa41a2fac69e9
6
+ metadata.gz: 3ccf3e88a3726c8c709b0df1c479952d85f08e787e2d1cf218eeef2a59f1f62128dbc27e534fb13c214b55f0c0c7644b418ec66d7502247316fe209c11f544b0
7
+ data.tar.gz: 2bb0be7f5e689e5aec743de2dc90d5bbb0bcf534ff76207afede9373eac35ae52073fcd63951859d4e656f7c227f646dc98fc12837fdf6689188db1311f035ba
data/README.md CHANGED
@@ -14,7 +14,7 @@ Intercept `method_missing` and do something useful with it.
14
14
  Add to your Gemfile:
15
15
 
16
16
  ```ruby
17
- gem 'method_found', '~> 0.1.3'
17
+ gem 'method_found', '~> 0.1.4'
18
18
  ```
19
19
 
20
20
  And bundle it.
@@ -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
- send(intercept_method, method_name, matches, *arguments, &block)
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
- matcher.call(method_name) && [method_name.to_s]
77
+ context.instance_exec(method_name, &matcher)
74
78
  else
75
- (matcher.to_sym == method_name) && [method_name.to_s]
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
@@ -1,3 +1,3 @@
1
1
  module MethodFound
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
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.3
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-04-23 00:00:00.000000000 Z
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