multi_block 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2011 Jan Lelis
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.rdoc ADDED
@@ -0,0 +1,95 @@
1
+ = MultiBlock
2
+
3
+ MultiBlock is a mini framework for passing multiple blocks to methods. It uses "named procs" to accomplish this in a nice way. The receiving method can either yield all blocks, or just call specific ones, identified by order or name.
4
+
5
+ These gem was build during a codebrawl contest. You might also take a look at the other entries: http://codebrawl.com/contests/methods-taking-multiple-blocks
6
+
7
+ == Setup
8
+
9
+ gem install multi_block
10
+
11
+ == Named Procs
12
+ A named proc acts like a normal proc, but has got a name attribute. You can create it by calling a method with the desired name on +proc+:
13
+
14
+ >> a = proc.even?{ |e| e.even? }
15
+ => #<NamedProc:0x00000001ffc340@(irb):1>
16
+ >> a.name
17
+ => :even?
18
+ >> a[42]
19
+ => false
20
+
21
+ In the same way, you can create lambdas:
22
+
23
+ >> b = lambda.doubler{ |e| e * 2 }
24
+ => #<NamedProc:0x000000020685e0@(irb):7 (lambda)>
25
+ >> b.name
26
+ => :doubler
27
+ >> b[21]
28
+ => 42
29
+ >> b.lambda?
30
+ => true
31
+
32
+ == MultiBlock Usage
33
+ === Defining methods that use multiple blocks
34
+
35
+ The first argument given to yield always defines the desired block(s). The other arguments get directly passed to the block(s). So these are example calls to the block:
36
+
37
+ yield # calls all given procs without args
38
+ yield :success # calls :success proc without args
39
+ yield :success, "Code Brawl!" # calls :success proc with message
40
+ yield 1 # calls first proc (:success in this case)
41
+ yield [:success, :bonus] # calls :success and :bonus without args
42
+ yield [:success, :bonus], "Code Brawl!" # calls both procs with same arg
43
+ yield success: "Code Brawl!", # calls each keyed proc,
44
+ error: [500, "Internal Brawl Error"] # values are the args
45
+
46
+ Consider these two example methods:
47
+
48
+ def ajax
49
+ yield rand(6) != 0 ? :success : :error # calls the :success block if everything worked well
50
+ end
51
+
52
+ def dice
53
+ random_number = rand(6)
54
+ yield random_number, random_number + 1 # calls the n-th block
55
+ end
56
+
57
+ === Calling methods with multiple blocks
58
+
59
+ It's done by calling the +blocks+ helper method:
60
+
61
+ ajax &blocks[
62
+ proc.success do puts "Yeah!" end,
63
+ proc.error do puts "Error..." end,
64
+ ]
65
+
66
+ The dice method could, for example, be called in this way:
67
+
68
+ dice &blocks[
69
+ proc{ ":(" },
70
+ proc{ ":/" },
71
+ proc{ ":O" },
72
+ proc{ ":b" },
73
+ proc{ ":P" },
74
+ proc{ rand(42) != 0 ? ":)" : ":D"},
75
+ ]
76
+
77
+ == Bonus sugar: Array extension
78
+
79
+ If you like the slim <tt>&to_proc</tt> operator, you can further optimize the syntax by calling:
80
+
81
+ Array.send :include, MultiBlock::Array
82
+
83
+ Now, it's getting real hot:
84
+
85
+ do_something, some_argument, &[
86
+ proc.easy_way do
87
+ # do it the easy way
88
+ end,
89
+
90
+ proc.complex_way do
91
+ # use complex heuristics, etc.
92
+ end,
93
+ ]
94
+
95
+ == J-_-L
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ require 'named_proc'
3
+
4
+ module MultiBlock
5
+ # multiple block transformation method,
6
+ # sorry for the method length and the code dup ;)
7
+ def self.[](*proc_array)
8
+ # Create hash representation, proc_array will still get used sometimes
9
+ proc_hash = {}
10
+ proc_array.each{ |proc|
11
+ proc_hash[proc.name] = proc if proc.respond_to?(:name)
12
+ }
13
+
14
+ # Build yielder proc
15
+ Proc.new{ |*proc_names_and_args|
16
+ if proc_names_and_args.empty? # call all procs
17
+ ret = proc_array.map(&:call)
18
+
19
+ proc_array.size == 1 ? ret.first : ret
20
+ else
21
+ proc_names, *proc_args = *proc_names_and_args
22
+
23
+ if proc_names.is_a? Hash # keys: proc_names, values: args
24
+ proc_names.map{ |proc_name, proc_args|
25
+ proc = proc_name.is_a?(Integer) ? proc_array[proc_name] : proc_hash[proc_name.to_sym]
26
+ proc or raise LocalJumpError, "wrong block name given (#{proc_name})"
27
+
28
+ [proc, Array(proc_args)]
29
+ }.map{ |proc, proc_args|
30
+ proc.call(*proc_args)
31
+ }
32
+
33
+ else
34
+ ret = Array(proc_names).map{ |proc_name|
35
+ proc = proc_name.is_a?(Integer) ? proc_array[proc_name] : proc_hash[proc_name.to_sym]
36
+ proc or raise LocalJumpError, "wrong block name given (#{proc_name})"
37
+
38
+ [proc, Array(proc_args)]
39
+ }.map{ |proc, proc_args|
40
+ proc.call(*proc_args)
41
+ }
42
+
43
+ ret.size == 1 ? ret.first : ret
44
+
45
+ end
46
+ end
47
+ }
48
+ end
49
+
50
+ # low level mixins
51
+ module Object
52
+ private
53
+
54
+ # to_proc helper, see README
55
+ def blocks
56
+ MultiBlock#[]
57
+ end
58
+
59
+ # alias procs blocks
60
+ # alias b blocks
61
+ end
62
+
63
+ ::Object.send :include, ::MultiBlock::Object
64
+
65
+ # Bonus array mixin (if you want to)
66
+ module Array
67
+ # see README for an example
68
+ def to_proc
69
+ ::MultiBlock[*self]
70
+ end
71
+ end
72
+
73
+ # ::Array.send :include, MultiBlock::Array
74
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'rubygems' unless defined? Gem
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "multi_block"
6
+ s.version = 1.0
7
+ s.authors = ["Jan Lelis"]
8
+ s.email = "mail@janlelis.de"
9
+ s.homepage = "https://gist.github.com/4b2f5fd0b45118e46d0f"
10
+ s.summary = 'MultiBlock is a mini framework for passing multiple blocks to methods.'
11
+ s.description = 'MultiBlock is a mini framework for passing multiple blocks to methods. It uses "named procs" to accomplish this in a nice way. The receiving method can either yield all blocks, or just call specific ones, identified by order or name. '
12
+ s.required_ruby_version = '>= 1.9.2'
13
+ s.files = Dir.glob %w{multi_block.gemspec lib/multi_block.rb spec/multi_block_spec.rb}
14
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
15
+ s.license = 'MIT'
16
+ s.add_dependency 'named_proc'
17
+ s.add_development_dependency 'rspec'
18
+ s.add_development_dependency 'rspec-core'
19
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ require_relative '../lib/multi_block'
3
+
4
+ describe "blocks" do
5
+ it "returns the MutliBlock constant (for calling [] on it)" do
6
+ blocks.should == MultiBlock
7
+ end
8
+ end
9
+
10
+ describe MultiBlock, "#[]" do
11
+ it "yield without args: calls every block and returns array of results" do
12
+ def null
13
+ yield
14
+ end
15
+
16
+ null(&blocks[
17
+ proc{5},
18
+ proc{6},
19
+ ]).should == [5,6]
20
+ end
21
+
22
+ it "yield with symbol: calls the specified proc, other args get passed" do
23
+ def symbol
24
+ yield :success, "Code Brawl!"
25
+ end
26
+
27
+ symbol(&blocks[
28
+ proc{5},
29
+ proc.success{|e| e.swapcase},
30
+ proc.error{6},
31
+ ]).should == "cODE bRAWL!"
32
+ end
33
+
34
+ it 'yield with symbol: raises LocalJumpError if proc name is wrong' do
35
+ def wrong_name
36
+ yield :wrong, "Code Brawl!"
37
+ end
38
+
39
+ proc do
40
+ wrong_name(&blocks[
41
+ proc{5},
42
+ proc.success{|e| e.swapcase},
43
+ proc.error{6},
44
+ ])
45
+ end.should raise_exception(LocalJumpError)
46
+ end
47
+
48
+ it "yield with integer: calls the n-th proc, other args get passed" do
49
+ def integer
50
+ yield 2
51
+ end
52
+
53
+ integer(&blocks[
54
+ proc{5},
55
+ proc.success{|e| e.swapcase},
56
+ proc.error{6},
57
+ ]).should == 6
58
+ end
59
+
60
+ it "yield with array: calls all procs, indentified by symbol or integer, other args get passed" do
61
+ def array
62
+ yield [:success, :error], "Code Brawl!"
63
+ end
64
+
65
+ array(&blocks[
66
+ proc{5},
67
+ proc.success{|e| e.swapcase},
68
+ proc.error{|e| e.downcase},
69
+ ]).should == ["cODE bRAWL!", "code brawl!"]
70
+ end
71
+
72
+ it "yield with hash: takes keys as proc names and passes values as proc args" do
73
+ def hash
74
+ yield success: "Code Brawl!", error: [500, "Internal Brawl Error"]
75
+ end
76
+
77
+ hash(&blocks[
78
+ proc{5},
79
+ proc.success{|e| e.swapcase},
80
+ proc.error{|no, msg| "Error #{no}: #{msg}"},
81
+ ]).sort.should == ["Error 500: Internal Brawl Error", "cODE bRAWL!"]
82
+ end
83
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multi_block
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jan Lelis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-24 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: named_proc
16
+ requirement: &13586400 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *13586400
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &13583220 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *13583220
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec-core
38
+ requirement: &13582300 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *13582300
47
+ description: ! 'MultiBlock is a mini framework for passing multiple blocks to methods.
48
+ It uses "named procs" to accomplish this in a nice way. The receiving method can
49
+ either yield all blocks, or just call specific ones, identified by order or name. '
50
+ email: mail@janlelis.de
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files:
54
+ - README.rdoc
55
+ - LICENSE.txt
56
+ files:
57
+ - multi_block.gemspec
58
+ - lib/multi_block.rb
59
+ - spec/multi_block_spec.rb
60
+ - README.rdoc
61
+ - LICENSE.txt
62
+ homepage: https://gist.github.com/4b2f5fd0b45118e46d0f
63
+ licenses:
64
+ - MIT
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: 1.9.2
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.11
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: MultiBlock is a mini framework for passing multiple blocks to methods.
87
+ test_files: []