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 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