multi_block 1.0

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