method_found 0.1.1 → 0.1.6

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
- SHA1:
3
- metadata.gz: 54697c45ed4ce6071e6c36e15b8aa1a7ec0a2ace
4
- data.tar.gz: 366c25aaff318c8033e51ebff45c6415c5c32651
2
+ SHA256:
3
+ metadata.gz: 95397b45051b6b4b33b65a371f9115455f7006398d726678bd045e6a3bb32f5b
4
+ data.tar.gz: b4cd923223329c88bd1c3e3ce2281f14975a44ecd2c99711251626cc62c3b0e4
5
5
  SHA512:
6
- metadata.gz: d78c7e39e4eb121df39f2f9f0b6c68c21cc0b1465bde81220b38bfa74b6d5d032e3f311fcfaf389d5eb40daccd81567d43b1c3c42f2ff749e435db1fc8a24ae1
7
- data.tar.gz: 9f3a8ef4238dcd2575dd5a55992835ac77033f5a4739db70f7b1624632e7ba4916d5cd4142aede903ed70d9cc9d342ff208d6161e494573c1b4714dfff10c3c2
6
+ metadata.gz: b0d3e0960d80193f87d0d690cd18081caae5a10fdbcd85a9cd85c914a2b65c7192ec4a6ada6cb5f1f57bb0bb8c489dddf9edc233bb1fc63d2e3fb078684fdd4d
7
+ data.tar.gz: 8a7acaa4dffeb0a638dfb7e47449c5e8820530d3958149ac69abbb2186c6d9d907529e47b78e3d3b40ae15a77d8603b3b5445d67a3b6eec21aecee24252997ef
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.1'
17
+ gem 'method_found', '~> 0.1.6'
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
-
@@ -1,8 +1,12 @@
1
+ require "securerandom"
2
+
1
3
  require "method_found/version"
4
+ require "method_found/builder"
2
5
  require "method_found/interceptor"
6
+ require "method_found/attribute_methods"
3
7
 
4
8
  module MethodFound
5
9
  def self.new(*args, &block)
6
- Interceptor.new(*args, &block)
10
+ Builder.new(*args, &block)
7
11
  end
8
12
  end
@@ -0,0 +1,91 @@
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
+ @regex = /\A(?:#{Regexp.escape(prefix)})(.*)(?:#{Regexp.escape(suffix)})\z/
47
+ @method_missing_target = method_missing_target = "#{prefix}attribute#{suffix}"
48
+ @method_name = "#{prefix}%s#{suffix}"
49
+
50
+ super to_proc do |_, attr_name, *args, &block|
51
+ send(method_missing_target, attr_name, *args, &block)
52
+ end
53
+ end
54
+
55
+ def define_attribute_methods(*attr_names)
56
+ handler = @method_missing_target
57
+ attr_names.each do |attr_name|
58
+ define_method method_name(attr_name) do |*arguments, &block|
59
+ send(handler, attr_name, *arguments, &block)
60
+ end
61
+ end
62
+ end
63
+
64
+ def alias_attribute(new_name, old_name)
65
+ handler = method_name(old_name)
66
+ define_method method_name(new_name) do |*arguments, &block|
67
+ send(handler, *arguments, &block)
68
+ end
69
+ end
70
+
71
+ def to_proc
72
+ regex = @regex
73
+ proc do |method_name|
74
+ (matches = regex.match(method_name)) &&
75
+ (method_name != :attributes) &&
76
+ respond_to?(:attributes) &&
77
+ (attributes.keys & matches.captures).first
78
+ end
79
+ end
80
+
81
+ def inspect
82
+ "<#{self.class.name}: #{@regex.inspect}>"
83
+ end
84
+
85
+ private
86
+
87
+ def method_name(attr_name)
88
+ @method_name % attr_name
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,79 @@
1
+ require "method_found/attribute_interceptor"
2
+
3
+ module MethodFound
4
+ =begin
5
+
6
+ Re-implementation of ActiveModel::AttributeMethods from Rails using
7
+ {MethodFound::Interceptor} modules instead of class variables.
8
+
9
+ @example
10
+ class Person < Struct.new(:attributes)
11
+ include MethodFound::AttributeMethods
12
+
13
+ attribute_method_suffix ''
14
+ attribute_method_suffix '='
15
+ attribute_method_suffix '_contrived?'
16
+ attribute_method_prefix 'clear_'
17
+ attribute_method_affix prefix: 'reset_', suffix: '_to_default!'
18
+
19
+ define_attribute_methods :name
20
+
21
+ def initialize(attributes = {})
22
+ super(attributes)
23
+ end
24
+
25
+ private
26
+
27
+ def attribute_contrived?(attr)
28
+ true
29
+ end
30
+
31
+ def clear_attribute(attr)
32
+ send("#{attr}=", nil)
33
+ end
34
+
35
+ def reset_attribute_to_default!(attr)
36
+ send("#{attr}=", 'Default Name')
37
+ end
38
+
39
+ def attribute(attr)
40
+ attributes[attr]
41
+ end
42
+
43
+ def attribute=(attr, value)
44
+ attributes[attr] = value
45
+ end
46
+ end
47
+
48
+ =end
49
+ module AttributeMethods
50
+ def self.included(base)
51
+ base.include(AttributeInterceptor.new)
52
+ base.instance_eval do
53
+ def attribute_method_prefix(*prefixes)
54
+ prefixes.each { |prefix| include AttributeInterceptor.new(prefix: prefix) }
55
+ end
56
+
57
+ def attribute_method_suffix(*suffixes)
58
+ suffixes.each { |suffix| include AttributeInterceptor.new(suffix: suffix) }
59
+ end
60
+
61
+ def attribute_method_affix(*affixes)
62
+ affixes.each { |affix| include AttributeInterceptor.new(prefix: affix[:prefix], suffix: affix[:suffix]) }
63
+ end
64
+
65
+ def define_attribute_methods(*attr_names)
66
+ ancestors.each do |ancestor|
67
+ ancestor.define_attribute_methods(*attr_names) if ancestor.is_a?(AttributeInterceptor)
68
+ end
69
+ end
70
+
71
+ def alias_attribute(new_name, old_name)
72
+ ancestors.each do |ancestor|
73
+ ancestor.alias_attribute(new_name, old_name) if ancestor.is_a?(AttributeInterceptor)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -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
31
+ @interceptors = []
32
+ super
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,22 +1,91 @@
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
- def initialize(regex, &intercept_block)
10
+ attr_reader :matcher
11
+
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?
16
+ end
17
+
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
+
4
29
  define_method :method_missing do |method_name, *arguments, &method_block|
5
- if matches = regex.match(method_name)
6
- instance_exec(method_name, matches, *arguments, &intercept_block)
30
+ if matches = matcher_.match(method_name, self)
31
+ method_cacher.(method_name, matches)
32
+ send(method_name, *arguments, &method_block)
7
33
  else
8
34
  super(method_name, *arguments, &method_block)
9
35
  end
10
36
  end
11
37
 
12
38
  define_method :respond_to_missing? do |method_name, include_private = false|
13
- (method_name =~ regex) || super(method_name, include_private)
39
+ if matches = matcher_.match(method_name, self)
40
+ method_cacher.(method_name, matches)
41
+ else
42
+ super(method_name, include_private)
43
+ end
44
+ end
45
+ end
46
+
47
+ def inspect
48
+ klass = self.class
49
+ name = klass.name || klass.inspect
50
+ "#<#{name}: #{matcher.inspect}>"
51
+ end
52
+
53
+ private
54
+
55
+ def cache_method(method_name, matches)
56
+ intercept_method, matcher = @intercept_method, @matcher
57
+ define_method method_name do |*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)
63
+ end
64
+ end
65
+
66
+ def assign_intercept_method(&intercept_block)
67
+ @intercept_method ||= "__intercept_#{SecureRandom.hex}".freeze.tap do |method_name|
68
+ define_method method_name, &intercept_block
69
+ end
70
+ end
71
+
72
+ class Matcher < Struct.new(:matcher)
73
+ def match(method_name, context)
74
+ if matcher.is_a?(Regexp)
75
+ matcher.match(method_name)
76
+ elsif matcher.respond_to?(:call)
77
+ context.instance_exec(method_name, &matcher)
78
+ else
79
+ (matcher.to_sym == method_name)
80
+ end
81
+ end
82
+
83
+ def proc?
84
+ matcher.is_a?(Proc)
14
85
  end
15
86
 
16
- define_method :inspect do
17
- klass = self.class
18
- name = klass.name || klass.inspect
19
- "#<#{name}: #{regex.inspect}>"
87
+ def inspect
88
+ matcher.inspect
20
89
  end
21
90
  end
22
91
  end
@@ -1,3 +1,3 @@
1
1
  module MethodFound
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.6"
3
3
  end
@@ -27,7 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
29
 
30
- spec.add_development_dependency "bundler", "~> 1.12"
31
- spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rake", "~> 12.3"
32
31
  spec.add_development_dependency "rspec", "~> 3.0"
33
32
  end
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: method_found
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Salzberg
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-04-17 00:00:00.000000000 Z
11
+ date: 2020-12-29 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.12'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.12'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rake
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - "~>"
32
18
  - !ruby/object:Gem::Version
33
- version: '10.0'
19
+ version: '12.3'
34
20
  type: :development
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
- version: '10.0'
26
+ version: '12.3'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: rspec
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,7 +38,7 @@ dependencies:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
40
  version: '3.0'
55
- description:
41
+ description:
56
42
  email:
57
43
  - chris@dejimata.com
58
44
  executables: []
@@ -71,6 +57,9 @@ files:
71
57
  - bin/console
72
58
  - bin/setup
73
59
  - lib/method_found.rb
60
+ - lib/method_found/attribute_interceptor.rb
61
+ - lib/method_found/attribute_methods.rb
62
+ - lib/method_found/builder.rb
74
63
  - lib/method_found/interceptor.rb
75
64
  - lib/method_found/version.rb
76
65
  - method_found.gemspec
@@ -78,7 +67,7 @@ homepage: https://github.com/shioyama/method_found
78
67
  licenses:
79
68
  - MIT
80
69
  metadata: {}
81
- post_install_message:
70
+ post_install_message:
82
71
  rdoc_options: []
83
72
  require_paths:
84
73
  - lib
@@ -93,9 +82,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
82
  - !ruby/object:Gem::Version
94
83
  version: '0'
95
84
  requirements: []
96
- rubyforge_project:
97
- rubygems_version: 2.4.5.1
98
- signing_key:
85
+ rubygems_version: 3.0.6
86
+ signing_key:
99
87
  specification_version: 4
100
88
  summary: Gem to intercept method_missing calls and do something useful.
101
89
  test_files: []