method_found 0.1.2 → 0.1.3

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: 9de7a0b0880e53f1c96603d88fa416c025a4e29d
4
- data.tar.gz: 4b65ed2fbf1926df69dc140341c677240fcc3a4d
3
+ metadata.gz: c80b59b4cfa2a683e9eaf250f975cebeff2594f0
4
+ data.tar.gz: 7004c79a99fddd803d098f4ebabe5541d8ebfeae
5
5
  SHA512:
6
- metadata.gz: 0ad77664bf164a317b348a0af8822ec3532b2b688909fb216635abf7dacba6b70a185ee8cf8b53025972969ff7e96e42f1babd4a999d143a22700d1ca62b3f32
7
- data.tar.gz: d35e820982307478a88e09ccf0b7c2c35725123f429d9795466c3b7451a51e278e395c73f0b0710e2c8d38f81797da7653503da5d090efd9796f454bb4cc0c2e
6
+ metadata.gz: ccb77ec85e8d6b98b66315c53853088308ba74a760dabe638499717f597ccdf9c2dcf44378cb1c4c1a26cf35202963fc8116c6154d785581f7a43b7dee4e96f6
7
+ data.tar.gz: b76fbe65a0a186bb5f361ab251c9d514e20b020aa0ac0949dfacfcf832e8a997392c519c23e85615227599ec0ed97feeedf5facdd1372792692fa41a2fac69e9
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+
11
+ Guardfile
data/Guardfile CHANGED
@@ -39,32 +39,4 @@ guard :rspec, cmd: "bundle exec rspec" do
39
39
  # Ruby files
40
40
  ruby = dsl.ruby
41
41
  dsl.watch_spec_files_for(ruby.lib_files)
42
-
43
- # Rails files
44
- rails = dsl.rails(view_extensions: %w(erb haml slim))
45
- dsl.watch_spec_files_for(rails.app_files)
46
- dsl.watch_spec_files_for(rails.views)
47
-
48
- watch(rails.controllers) do |m|
49
- [
50
- rspec.spec.call("routing/#{m[1]}_routing"),
51
- rspec.spec.call("controllers/#{m[1]}_controller"),
52
- rspec.spec.call("acceptance/#{m[1]}")
53
- ]
54
- end
55
-
56
- # Rails config changes
57
- watch(rails.spec_helper) { rspec.spec_dir }
58
- watch(rails.routes) { "#{rspec.spec_dir}/routing" }
59
- watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
60
-
61
- # Capybara features specs
62
- watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
63
- watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
64
-
65
- # Turnip features and steps
66
- watch(%r{^spec/acceptance/(.+)\.feature$})
67
- watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
68
- Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
69
- end
70
42
  end
data/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  [gem]: https://rubygems.org/gems/method_found
7
7
  [travis]: https://travis-ci.org/shioyama/method_found
8
+ [docs]: http://www.rubydoc.info/gems/method_found
8
9
 
9
10
  Intercept `method_missing` and do something useful with it.
10
11
 
@@ -13,23 +14,29 @@ Intercept `method_missing` and do something useful with it.
13
14
  Add to your Gemfile:
14
15
 
15
16
  ```ruby
16
- gem 'method_found', '~> 0.1.2'
17
+ gem 'method_found', '~> 0.1.3'
17
18
  ```
18
19
 
19
20
  And bundle it.
20
21
 
21
22
  ## Usage
22
23
 
23
- Include an instance of `MethodFound` with a regex to match and block which
24
- takes the method name, regex matches, and arguments and does something with it:
24
+ Include an instance of `MethodFound::Builder` with a block defining all
25
+ patterns to match. Identify a pattern with the `intercept` method, like this:
25
26
 
26
27
  ```ruby
27
28
  class Foo
28
- include(MethodFound.new(/\Asay_([a-z]+)/Z/) do |method_name, matches, *arguments|
29
- "#{matches[0]}!"
30
- end)
29
+ include MethodFound::Builder.new {
30
+ intercept /\Asay_([a-z]+)\Z/ do |method_name, matches, *arguments, &block|
31
+ "#{matches[1]}!"
32
+ end
33
+ }
31
34
  end
35
+ ```
36
+
37
+ Now you can say things:
32
38
 
39
+ ```ruby
33
40
  foo = Foo.new
34
41
  foo.say_hello
35
42
  #=> "hello!"
@@ -37,7 +44,13 @@ foo.say_bye
37
44
  #=> "bye!"
38
45
  ```
39
46
 
47
+ That's it!
48
+
49
+ ## More Information
50
+
51
+ - [Github repository](https://www.github.com/shioyama/method_found)
52
+ - [API documentation][docs]
53
+
40
54
  ## License
41
55
 
42
56
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
43
-
@@ -0,0 +1,40 @@
1
+ module MethodFound
2
+ =begin
3
+
4
+ Creates set of interceptors to include into a class.
5
+
6
+ @example
7
+ class Post
8
+ builder = MethodFound::Builder.new {
9
+ intercept /\Asay_([a-z]+)\Z/ do |method_name, matches, *arguments|
10
+ "#{matches[1]}!"
11
+ end
12
+
13
+ intercept /\Ayell_([a-z]+)\Z/ do |method_name, matches, *arguments|
14
+ "#{matches[1]}!!!"
15
+ end
16
+ }
17
+ end
18
+
19
+ foo = Foo.new
20
+ foo.say_hello
21
+ #=> "hello!"
22
+ foo.yell_hello
23
+ #=> "hello!!!"
24
+ =end
25
+ class Builder < Module
26
+ attr_reader :interceptors
27
+
28
+ # @yield Yields builder as context to block, to allow calling builder
29
+ # methods to create interceptors in included class.
30
+ def initialize(&block)
31
+ @interceptors = []
32
+ instance_eval &block
33
+ end
34
+
35
+ def intercept(*args, &block)
36
+ @interceptors.push(interceptor = Interceptor.new(*args, &block))
37
+ include interceptor
38
+ end
39
+ end
40
+ end
@@ -1,30 +1,84 @@
1
1
  module MethodFound
2
+ =begin
3
+
4
+ Class for intercepting method calls using method_missing. Initialized by
5
+ passing in a matcher object which can be a Regexp, a proc or lambda, or a
6
+ string/symbol.
7
+
8
+ =end
2
9
  class Interceptor < Module
3
- attr_reader :regex
10
+ attr_reader :matcher
4
11
 
5
- def initialize(regex = nil, &intercept_block)
6
- define_method_missing(regex, &intercept_block) unless regex.nil?
12
+ # Creates an interceptor module to include into a class.
13
+ # @param (see #define_method_missing)
14
+ def initialize(matcher = nil, &intercept_block)
15
+ define_method_missing(matcher, &intercept_block) unless matcher.nil?
7
16
  end
8
17
 
9
- def define_method_missing(regex, &intercept_block)
10
- @regex = regex
18
+ # Define method_missing and respond_to_missing? on this interceptor. Can be
19
+ # called after interceptor has been created.
20
+ # @param [Regexp,Proc,String,Symbol] matcher Matcher for intercepting
21
+ # method calls.
22
+ # @yield [method_name, matches, &block] Yiels method_name matched, set of
23
+ # matches returned from matcher, and block passed to method when called.
24
+ def define_method_missing(matcher, &intercept_block)
25
+ @matcher = matcher_ = Matcher.new(matcher)
26
+ assign_intercept_method(&intercept_block)
27
+ method_cacher = method(:cache_method)
28
+
11
29
  define_method :method_missing do |method_name, *arguments, &method_block|
12
- if matches = regex.match(method_name)
13
- instance_exec(method_name, matches, *arguments, &intercept_block)
30
+ if matches = matcher_.match(method_name)
31
+ method_cacher.(method_name, matches)
32
+ send(method_name, *arguments, &method_block)
14
33
  else
15
34
  super(method_name, *arguments, &method_block)
16
35
  end
17
36
  end
18
37
 
19
38
  define_method :respond_to_missing? do |method_name, include_private = false|
20
- (method_name =~ regex) || super(method_name, include_private)
39
+ if matches = matcher_.match(method_name)
40
+ method_cacher.(method_name, matches)
41
+ else
42
+ super(method_name, include_private)
43
+ end
21
44
  end
22
45
  end
23
46
 
24
47
  def inspect
25
48
  klass = self.class
26
49
  name = klass.name || klass.inspect
27
- "#<#{name}: #{regex.inspect}>"
50
+ "#<#{name}: #{matcher.inspect}>"
51
+ end
52
+
53
+ private
54
+
55
+ def cache_method(method_name, matches)
56
+ intercept_method = @intercept_method
57
+ define_method method_name do |*arguments, &block|
58
+ send(intercept_method, method_name, matches, *arguments, &block)
59
+ end
60
+ end
61
+
62
+ def assign_intercept_method(&intercept_block)
63
+ @intercept_method ||= "__intercept_#{SecureRandom.hex}".freeze.tap do |method_name|
64
+ define_method method_name, &intercept_block
65
+ end
66
+ end
67
+
68
+ class Matcher < Struct.new(:matcher)
69
+ def match(method_name)
70
+ if matcher.is_a?(Regexp)
71
+ matcher.match(method_name)
72
+ elsif matcher.respond_to?(:call)
73
+ matcher.call(method_name) && [method_name.to_s]
74
+ else
75
+ (matcher.to_sym == method_name) && [method_name.to_s]
76
+ end
77
+ end
78
+
79
+ def inspect
80
+ matcher.inspect
81
+ end
28
82
  end
29
83
  end
30
84
  end
@@ -0,0 +1,109 @@
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
@@ -1,3 +1,3 @@
1
1
  module MethodFound
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
data/lib/method_found.rb CHANGED
@@ -1,8 +1,11 @@
1
+ require "securerandom"
2
+
1
3
  require "method_found/version"
4
+ require "method_found/builder"
2
5
  require "method_found/interceptor"
3
6
 
4
7
  module MethodFound
5
8
  def self.new(*args, &block)
6
- Interceptor.new(*args, &block)
9
+ Builder.new(*args, &block)
7
10
  end
8
11
  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.2
4
+ version: 0.1.3
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-18 00:00:00.000000000 Z
11
+ date: 2017-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,7 +71,9 @@ files:
71
71
  - bin/console
72
72
  - bin/setup
73
73
  - lib/method_found.rb
74
+ - lib/method_found/builder.rb
74
75
  - lib/method_found/interceptor.rb
76
+ - lib/method_found/simple_model.rb
75
77
  - lib/method_found/version.rb
76
78
  - method_found.gemspec
77
79
  homepage: https://github.com/shioyama/method_found