method_found 0.1.1 → 0.1.6

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
- 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: []