missingly 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in method_missing_improved.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Thijs de Vries
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Missingly
2
+
3
+ A DSL for handling method\_missing hooks.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'missingly'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install missingly
18
+
19
+ ## Usage
20
+
21
+ ### Use regular expression matching
22
+
23
+ ```ruby
24
+ class ArrayWithHashes
25
+ include Missingly::Matchers
26
+
27
+ handle_missingly /^find_by_(\w+)$/ do |matches, *args, &block|
28
+ fields = matches[1].split("_and_")
29
+ hashes.find do |hash|
30
+ fields.inject(true) do |fields_match, field|
31
+ index_of_field = fields.index(field)
32
+ arg_for_field = args[index_of_field]
33
+
34
+ fields_match = fields_match && hash[field.to_sym] == arg_for_field
35
+ break false unless fields_match
36
+ true
37
+ end
38
+ end
39
+ end
40
+
41
+ handle_missingly /^find_all_by_(\w+)$/ do |matches, *args, &block|
42
+ fields = matches[1].split("_and_")
43
+ hashes.find_all do |hash|
44
+ fields.inject(true) do |fields_match, field|
45
+ index_of_field = fields.index(field)
46
+ arg_for_field = args[index_of_field]
47
+
48
+ fields_match = fields_match && hash[field.to_sym] == arg_for_field
49
+ break false unless fields_match
50
+ true
51
+ end
52
+ end
53
+ end
54
+
55
+ attr_reader :hashes
56
+
57
+ def initialize(hashes)
58
+ @hashes = hashes
59
+ end
60
+ end
61
+
62
+ hashes = [
63
+ { id: 1, name: 'Pat', gender: 'f' },
64
+ { id: 2, name: 'Pat', gender: 'm' },
65
+ { id: 3, name: 'Steve', gender: 'm' },
66
+ { id: 4, name: 'Sue', gender: 'f' },
67
+ ]
68
+
69
+ instance = ArrayWithHashes.new(hashes)
70
+ instance.find_by_name_and_gender('Pat', 'm') # { id: 2, name: 'Pat', gender: 'm' }
71
+ instance.find_all_by_name('Pat') # both male and female Pat's
72
+ instance.respond_to?(:find_by_name_and_gender) # true
73
+ instance.method(:find_by_name_and_gender) # method object
74
+ ```
75
+
76
+ ### Use array matching
77
+
78
+ ```ruby
79
+ class NetJSON
80
+ include Missingly::Matchers
81
+
82
+ handle_missingly [:get, :put, :post, :delete] do |method_name, url, params|
83
+ uri = URI.parse(url)
84
+
85
+ requester = Net::HTTP.new(uri.host, uri.port)
86
+ request = "Net::HTTP::#{method_name.to_s.classify}".constantize.new(uri.path)
87
+
88
+ request.body = params.to_json
89
+
90
+ requester.request(request)
91
+ end
92
+ end
93
+
94
+ requester = NetJSON.new
95
+ requester.get 'http://www.example.com/some_path/', {first_name: 'John'}
96
+ requester.put 'http://www.example.com/some_resource/1/', {admin: true}
97
+ ```
98
+
99
+ ### Use for delegation
100
+
101
+ ```ruby
102
+ class UserDecorator
103
+ include Missingly::Matchers
104
+
105
+ handle_missingly [:roles], to: :user
106
+
107
+ def can_edit?
108
+ roles.include?(:editor)
109
+ end
110
+ end
111
+ ```
112
+
113
+ ## Contributing
114
+
115
+ 1. Fork it
116
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
117
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
118
+ 4. Push to the branch (`git push origin my-new-feature`)
119
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,36 @@
1
+ module Missingly
2
+ class ArrayMatcher
3
+ attr_reader :array, :options, :method_block
4
+
5
+ def initialize(array, options, method_block)
6
+ @array, @options, @method_block = array, options, method_block
7
+ end
8
+
9
+ def should_respond_to?(name)
10
+ array.include?(name)
11
+ end
12
+
13
+ def handle(instance, method_name, *args, &block)
14
+ if method_block
15
+ sub_name = "#{method_name}_with_method_name"
16
+
17
+ instance.class._define_method method_name do |*the_args, &the_block|
18
+ public_send(sub_name, method_name, *the_args, &the_block)
19
+ end
20
+ instance.class._define_method(sub_name, &method_block)
21
+
22
+ instance.public_send(method_name, *args, &block)
23
+ elsif options[:to]
24
+ instance.class.class_eval <<-CODE
25
+ def #{method_name}(*args, &block)
26
+ #{options[:to]}.#{method_name}(*args, &block)
27
+ end
28
+ CODE
29
+
30
+ instance.public_send(method_name, *args, &block)
31
+ else
32
+ raise ArgumentError, "either block, or to option should be passed"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ module Missingly
2
+ module Matchers
3
+ module ClassMethods
4
+ def handle_missingly(regular_expression_or_array, options={}, &block)
5
+ case regular_expression_or_array
6
+ when Array then missingly_matchers << ArrayMatcher.new(regular_expression_or_array, options, block)
7
+ when Regexp then missingly_matchers << RegexMatcher.new(regular_expression_or_array, options, block)
8
+ end
9
+ end
10
+
11
+ def missingly_matchers
12
+ @missingly_matchers ||= []
13
+ end
14
+
15
+ def _define_method(*args, &block)
16
+ define_method(*args, &block)
17
+ end
18
+ end
19
+
20
+ def respond_to_missing?(method_name, include_all)
21
+ self.class.missingly_matchers.each do |matcher|
22
+ return true if matcher.should_respond_to?(method_name.to_sym)
23
+ end
24
+ super
25
+ end
26
+ private :respond_to_missing?
27
+
28
+ def method_missing(method_name, *args, &block)
29
+ self.class.missingly_matchers.each do |matcher|
30
+ next unless matcher.should_respond_to?(method_name)
31
+
32
+ return matcher.handle(self, method_name, *args, &block)
33
+ end
34
+ super
35
+ end
36
+
37
+ private
38
+
39
+ def self.included(klass)
40
+ klass.extend ClassMethods
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ module Missingly
2
+ class RegexMatcher
3
+ attr_reader :regex, :options, :method_block
4
+
5
+ def initialize(regex, options, method_block)
6
+ @regex, @options, @method_block = regex, options, method_block
7
+ end
8
+
9
+ def should_respond_to?(name)
10
+ regex.match(name)
11
+ end
12
+
13
+ def handle(instance, method_name, *args, &block)
14
+ if method_block
15
+ matches = regex.match method_name
16
+
17
+ sub_name = "#{method_name}_with_matches"
18
+ instance.class._define_method method_name do |*the_args, &the_block|
19
+ public_send(sub_name, matches, *the_args, &the_block)
20
+ end
21
+ instance.class._define_method(sub_name, &method_block)
22
+
23
+ instance.public_send(method_name, *args, &block)
24
+ elsif options[:to]
25
+ instance.class.class_eval <<-CODE
26
+ def #{method_name}(*args, &block)
27
+ #{options[:to]}.#{method_name}(*args, &block)
28
+ end
29
+ CODE
30
+
31
+ instance.public_send(method_name, *args, &block)
32
+ else
33
+ raise ArgumentError, "either block, or to option should be passed"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Missingly
2
+ VERSION = "0.0.1"
3
+ end
data/lib/missingly.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "missingly/version"
2
+ require "missingly/matchers"
3
+ require "missingly/array_matcher"
4
+ require "missingly/regex_matcher"
5
+
6
+ module Missingly
7
+
8
+ end
data/missingly.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'missingly/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "missingly"
8
+ spec.version = Missingly::VERSION
9
+ spec.authors = ["Thijs de Vries"]
10
+ spec.email = ["moger777@gmail.com"]
11
+ spec.description = %q{A DSL for defining method missing methods}
12
+ spec.summary = %q{A DSL for defining method missing methods}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "pry"
25
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ module Missingly
4
+ describe Matchers do
5
+ context "respond_to?" do
6
+ let(:our_class) do
7
+ Class.new do
8
+ include Missingly::Matchers
9
+
10
+ handle_missingly [:derp, :herp] do
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:instance) do
16
+ our_class.new
17
+ end
18
+
19
+ it "should respond to methods that are included in array" do
20
+ instance.respond_to?(:derp).should == true
21
+ instance.respond_to?(:herp).should == true
22
+ instance.respond_to?('herp').should == true
23
+ instance.respond_to?('fluffy_buffy_bunnies').should == false
24
+ end
25
+ end
26
+
27
+ context "method_missing" do
28
+ let(:our_class) do
29
+ Class.new do
30
+ include Missingly::Matchers
31
+
32
+ attr_accessor :method_name, :args, :block, :expected_self
33
+
34
+ handle_missingly [:derp] do |method_name, *args, &block|
35
+ @expected_self = self
36
+ @method_name = method_name
37
+ @args = args
38
+ @block = block
39
+ end
40
+ end
41
+ end
42
+
43
+ let(:instance) do
44
+ our_class.new
45
+ end
46
+
47
+ it "should also work with arrays, but just passes method name instead of match object" do
48
+ args = [1, 2, 3]
49
+ prock = Proc.new { puts 'foo' }
50
+ instance.derp(*args, &prock)
51
+
52
+ instance.expected_self.should == instance
53
+ instance.method_name.should == :derp
54
+ instance.args.should == args
55
+ instance.block.should == prock
56
+ end
57
+
58
+ it "should define the method on call preventing further method missing calls on same class" do
59
+ args = [1, 2, 3]
60
+ prock = Proc.new { puts 'foo' }
61
+ instance.derp(*args, &prock)
62
+ Method.should === instance.method(:derp)
63
+ end
64
+
65
+ it "should work with subsequent calls" do
66
+ args = [1, 2, 3]
67
+ prock = Proc.new { puts 'foo' }
68
+ instance.derp(*args, &prock)
69
+ instance.derp(*args, &prock)
70
+
71
+ instance.expected_self.should == instance
72
+ instance.method_name.should == :derp
73
+ instance.args.should == args
74
+ instance.block.should == prock
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ module Missingly
4
+ describe Matchers do
5
+ let(:our_class) do
6
+ Class.new do
7
+ include Missingly::Matchers
8
+
9
+ handle_missingly [:derp], to: :proxy
10
+ handle_missingly /^find_by_(\w+)$/, to: :proxy
11
+ end
12
+ end
13
+
14
+ let(:proxy){ stub }
15
+
16
+ let(:instance) do
17
+ i = our_class.new
18
+ i.stub(:proxy).and_return(proxy)
19
+ i
20
+ end
21
+
22
+ it "should delegate method to attribute passed to to option for arrays" do
23
+ args = [1, 2]
24
+ prock = Proc.new{ puts "Don't call" }
25
+
26
+ args_passed = nil
27
+ block_passed = nil
28
+ proxy.should_receive(:derp) do |*_args|
29
+ args_passed = _args.first(2)
30
+ block_passed = _args.last
31
+ end
32
+
33
+ instance.derp(*args, *prock)
34
+
35
+ args_passed.should == args
36
+ block_passed.should == prock
37
+ end
38
+
39
+ it "should delegate method to attribute passed to to option for regexes" do
40
+ args = [1, 2]
41
+ prock = Proc.new{ puts "Don't call" }
42
+
43
+ args_passed = nil
44
+ block_passed = nil
45
+ proxy.should_receive(:find_by_id) do |*_args|
46
+ args_passed = _args.first(2)
47
+ block_passed = _args.last
48
+ end
49
+
50
+ instance.find_by_id(*args, *prock)
51
+
52
+ args_passed.should == args
53
+ block_passed.should == prock
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ module Missingly
4
+ describe Matchers do
5
+ context "respond_to?" do
6
+ let(:our_class) do
7
+ Class.new do
8
+ include Missingly::Matchers
9
+
10
+ handle_missingly /^find_by_(\w+)$/ do
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:instance) do
16
+ our_class.new
17
+ end
18
+
19
+ it "should only respond to methods that match regular expression passed to missingly" do
20
+ instance.respond_to?("find_by_id").should == true
21
+ instance.respond_to?("fluffy_buffy_bunnies").should == false
22
+ end
23
+
24
+ it "should not make respond_to_missing? public" do
25
+ instance.respond_to?("respond_to_missing?").should == false
26
+ end
27
+
28
+ it "should also work with inheritance" do
29
+ foo = Class.new do
30
+ def respond_to_missing?(name, include_all)
31
+ name.to_s == 'this_should_work'
32
+ end
33
+ end
34
+
35
+ bar = Class.new(foo) do
36
+ include Missingly::Matchers
37
+
38
+ handle_missingly /foo/ do
39
+ end
40
+ end
41
+
42
+ bar.new.respond_to?('this_should_work').should == true
43
+ end
44
+
45
+ it "should work with multiple definitions" do
46
+ our_class.module_eval do
47
+
48
+ handle_missingly /foo/ do
49
+ end
50
+ end
51
+ instance.respond_to?('foo').should == true
52
+ instance.respond_to?('find_by_id').should == true
53
+ end
54
+ end
55
+
56
+ context "method_missing" do
57
+ let(:our_class) do
58
+ Class.new do
59
+ include Missingly::Matchers
60
+
61
+ attr_accessor :matched_text, :args, :block, :expected_self
62
+
63
+ handle_missingly /^find_by_(\w+)$/ do |matches, *args, &block|
64
+ @expected_self = self
65
+ @matched_text = matches[1]
66
+ @args = args
67
+ @block = block
68
+ end
69
+ end
70
+ end
71
+
72
+ let(:instance) do
73
+ our_class.new
74
+ end
75
+
76
+ it "should call method that matches regular expression on instances passing matches and args and block" do
77
+ args = [1, 2, 3]
78
+ prock = Proc.new { puts 'foo' }
79
+ instance.find_by_id_and_first_name(*args, &prock)
80
+
81
+ instance.expected_self.should == instance
82
+ instance.matched_text.should == 'id_and_first_name'
83
+ instance.args.should == args
84
+ instance.block.should == prock
85
+ end
86
+
87
+ it "should define the method on call preventing further method missing calls on same class" do
88
+ args = [1, 2, 3]
89
+ prock = Proc.new { puts 'foo' }
90
+ instance.find_by_id_and_first_name(*args, &prock)
91
+ Method.should === instance.method(:find_by_id_and_first_name)
92
+ end
93
+
94
+ it "should work with subsequent calls" do
95
+ args = [1, 2, 3]
96
+ prock = Proc.new { puts 'foo' }
97
+ instance.find_by_id_and_first_name(*args, &prock)
98
+ instance.find_by_id_and_first_name(*args, &prock)
99
+
100
+ instance.expected_self.should == instance
101
+ instance.matched_text.should == 'id_and_first_name'
102
+ instance.args.should == args
103
+ instance.block.should == prock
104
+ end
105
+
106
+ it "should also work with arrays, but just passes method name instead of match object" do
107
+ our_class.module_eval do
108
+ attr_reader :method_name
109
+ handle_missingly [:derp] do |method_name, *args, &block|
110
+ @expected_self = self
111
+ @method_name = method_name
112
+ @args = args
113
+ @block = block
114
+ end
115
+ end
116
+ args = [1, 2, 3]
117
+ prock = Proc.new { puts 'foo' }
118
+ instance.derp(*args, &prock)
119
+
120
+ instance.expected_self.should == instance
121
+ instance.method_name.should == :derp
122
+ instance.args.should == args
123
+ instance.block.should == prock
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,2 @@
1
+ require_relative '../lib/missingly'
2
+ require 'pry'
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: missingly
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Thijs de Vries
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.3'
21
+ none: false
22
+ requirement: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ none: false
28
+ prerelease: false
29
+ type: :development
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ none: false
38
+ requirement: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ none: false
44
+ prerelease: false
45
+ type: :development
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ none: false
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ none: false
60
+ prerelease: false
61
+ type: :development
62
+ - !ruby/object:Gem::Dependency
63
+ name: pry
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ none: false
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ none: false
76
+ prerelease: false
77
+ type: :development
78
+ description: A DSL for defining method missing methods
79
+ email:
80
+ - moger777@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE.txt
88
+ - README.md
89
+ - Rakefile
90
+ - lib/missingly.rb
91
+ - lib/missingly/array_matcher.rb
92
+ - lib/missingly/matchers.rb
93
+ - lib/missingly/regex_matcher.rb
94
+ - lib/missingly/version.rb
95
+ - missingly.gemspec
96
+ - spec/array_spec.rb
97
+ - spec/delegation_spec.rb
98
+ - spec/regex_spec.rb
99
+ - spec/spec_helper.rb
100
+ homepage: ''
101
+ licenses:
102
+ - MIT
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ none: false
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ none: false
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 1.8.24
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: A DSL for defining method missing methods
125
+ test_files:
126
+ - spec/array_spec.rb
127
+ - spec/delegation_spec.rb
128
+ - spec/regex_spec.rb
129
+ - spec/spec_helper.rb