multi_case 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=documentation
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Andrey Korzhuev <andrew@korzhuev.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included
12
+ in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,131 @@
1
+ multi_case [![Build Status](https://travis-ci.org/andrusha/multi_case.png)](https://travis-ci.org/andrusha/multi_case) [![Dependency Status](https://gemnasium.com/andrusha/multi_case.png)](https://gemnasium.com/andrusha/multi_case) [![Code Climate](https://codeclimate.com/github/andrusha/multi_case.png)](https://codeclimate.com/github/andrusha/multi_case)
2
+ ==========
3
+
4
+ An improved `case` operator for ruby which allows you to make a choice based on how variable changes (a state machine of ifs).
5
+
6
+ Installation
7
+ ------------
8
+
9
+ `gem install multi_case`
10
+
11
+ Usage
12
+ -----
13
+
14
+ ### Interfaces
15
+
16
+ There are two interfaces available, one is an unobtrusive module, which can be
17
+ included into your class like so:
18
+
19
+ ```ruby
20
+ require 'multi_case'
21
+
22
+ class Cookie
23
+ include MultiCase::API
24
+
25
+ def eat_it!
26
+ mutli_case weight_was, weight do
27
+ multi(150 => 0..50) { puts "That was a good bite!" }
28
+ multi(150 => 150..(+1.0/0.0)) { puts "Are you puked?!" }
29
+ end
30
+ end
31
+ end
32
+ ```
33
+
34
+ Another one is to extend your `Kernel` making `multi_case` function global:
35
+
36
+ ```ruby
37
+ require 'multi_case/core_ext'
38
+
39
+ mutli_case ENV['petals_was'], ENV['petal'] do
40
+ multi([] => (0..100).step(2)) { puts "Любит" }
41
+ multi([] => (1..100).step(2)) { puts "Не любит" }
42
+ end
43
+ ```
44
+
45
+ ### Features
46
+
47
+ Most common use-case is to make a decision based on how your variable changes,
48
+ it also might be used as an unbotrusive state-machine implementation. For
49
+ example, if you have a post and its state went from `:draft` to `:published`
50
+ you might want to notify all your subscribed users about that, which can be
51
+ implemented like so:
52
+
53
+ ```ruby
54
+ class Post
55
+ def notify
56
+ multi_case status_was, status do
57
+ multi([] => :draft) { Notification.needs_input(post).to_editors }
58
+ multi(:draft => :published) { Notification.new_post(post).to_subscribers }
59
+ multi([] => :destroyed) { Notification.destoyed_post(post).to_admins }
60
+ end
61
+ end
62
+ end
63
+ ```
64
+
65
+ It also returns values so it can be used to replace nested `case` statements:
66
+
67
+ ```ruby
68
+ beer_status = multi_case bottles_was, bottles do
69
+ multi(0 => 1..10) { :party_time }
70
+ multi(1..10 => 0) { :needs_refil }
71
+ end
72
+ ```
73
+
74
+ Since it uses case-matching (`===`) internally you can use all its goodiness:
75
+
76
+ ```ruby
77
+ multi_case x, y do
78
+ multi(String => /a+/i) { 0 }
79
+ multi(1..100 => [:x, :y, :z]) { 0 }
80
+ end
81
+ ```
82
+
83
+ ### Syntax
84
+
85
+ `multi_case` expect two values and block which contains matchers, first value
86
+ is matched against key of one-elemnt hash which is provided to `multi`,
87
+ second value is matched against value of said hash.
88
+
89
+ `multi` expect you to provide one-elemnt hash and block, if hash has array in it
90
+ then all array elements are matched against value sequentially and empty array
91
+ means "match-all". The block provided to multi is executed in case of correct
92
+ match and its result is what is returned by `multi_case`.
93
+
94
+ ### Implementation
95
+
96
+ The `mutli_case` statement is complete equivalent of nested `case` statements,
97
+ so these two snippets are intedent to work exactly the same:
98
+
99
+ ```ruby
100
+ result = multi_case old_value, new_value do
101
+ multi [:a, :b] => [:c, :d] { :e }
102
+ multi [:f, :g] => [:h, :i] { :j }
103
+ multi [] => [:k, :l] { :m }
104
+ multi [:n, :o] => [] { :p }
105
+ multi [:f, :g] => [:q, :r] { :s }
106
+ end
107
+ ```
108
+
109
+
110
+ ```ruby
111
+ result = case old_value
112
+ when :a, :b
113
+ case new_value
114
+ when :c, :d then :e
115
+ end
116
+ when :f, :g
117
+ case new_value
118
+ when :h, :i then :j
119
+ when :q, :r then :s
120
+ end
121
+ when :n, :o then :p
122
+ else
123
+ case new_value
124
+ when :k, :l then :m
125
+ end
126
+ end
127
+ ```
128
+
129
+ Copyright
130
+ =========
131
+ Copytright © 2013 Andrey Korzhuev. See LICENSE for details.
@@ -0,0 +1,30 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :test => ["rspec"]
8
+
9
+ require "multi_case"
10
+
11
+ PACKAGE = "#{MultiCase::PACKAGE}"
12
+ VERSION = "#{MultiCase::VERSION}"
13
+
14
+ task :package do
15
+ system "gem build #{PACKAGE}.gemspec"
16
+ end
17
+
18
+ task :install => :package do
19
+ Dir.chdir("pkg") do
20
+ system "gem install #{PACKAGE}-#{VERSION}"
21
+ end
22
+ end
23
+
24
+ task :release => :package do
25
+ Dir.chdir("pkg") do
26
+ system "gem push #{PACKAGE}-#{VERSION}"
27
+ end
28
+ end
29
+
30
+ task :default => :test
@@ -0,0 +1,10 @@
1
+ #
2
+ # An improved case operator which allow you to compare two
3
+ # variables at once, usually describing some kind of state change
4
+ #
5
+ module MultiCase
6
+ PACKAGE = 'multi_case'
7
+ VERSION = '0.0.1'
8
+ end
9
+
10
+ require 'multi_case/api'
@@ -0,0 +1,20 @@
1
+ require 'multi_case/dsl'
2
+
3
+ module MultiCase::API
4
+ protected
5
+
6
+ #
7
+ # Matches two parameters agains specified causes returning the result
8
+ # of user specified functions
9
+ # @param lhs [#===] Matched against left-hand side
10
+ # @param rhs [#===] Matches against right-hand side
11
+ # @param &block [Object] A function which contains number of `multi` clauses
12
+ #
13
+ # @return [Object] Value returned by first matching matching specified by user
14
+ def multi_case(lhs, rhs, &block)
15
+ dsl = MultiCase::DSL.new(lhs, rhs)
16
+ dsl.instance_eval(&block)
17
+
18
+ dsl.result
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ require 'multi_case'
2
+
3
+ #
4
+ # Makes multi_case a global method available at main:Object
5
+ # as well as inside any other class. BEWARE, this might make
6
+ # some modules behave unexpectedly because of name collision.
7
+ #
8
+ module Kernel
9
+ include MultiCase::API
10
+ alias_method :_multi_case, :multi_case
11
+
12
+ def multi_case(x, y, &block)
13
+ _multi_case(x, y, &block)
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ class MultiCase::DSL
2
+ #
3
+ # @param lhs [#===]
4
+ # @param rhs [#===]
5
+ #
6
+ def initialize(lhs, rhs)
7
+ @lhs = lhs
8
+ @rhs = rhs
9
+
10
+ @result = nil
11
+ end
12
+
13
+ attr_reader :result
14
+
15
+ #
16
+ # A single case to which @lhs & @rhs are matched against
17
+ # @param from_to [Hash] @lhs is matched against keys, @rhs against values,
18
+ # they both can be arrays
19
+ # @param &block [Object] User-specified result if both of parameters match
20
+ #
21
+ # @return [Object] Return-value of user-specified function
22
+ def multi(from_to, &block)
23
+ from = from_to.keys.flatten(1)
24
+ from = [@lhs] if from.empty? # catch-all on empty array
25
+
26
+ to = from_to.values.flatten(1)
27
+ to = [@rhs] if to.empty?
28
+
29
+ if from.product(to).map(&method(:param_equal)).any?
30
+ @result ||= block.call
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ #
37
+ # Case-compares tuple against @lhs and @rhs
38
+ # @param arr [Array] Tuple to match against
39
+ #
40
+ # @return [Boolean]
41
+ def param_equal(arr)
42
+ f, t = arr
43
+ (f === @lhs) && (t === @rhs)
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "multi_case"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = MultiCase::PACKAGE
7
+ s.version = MultiCase::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Andrey Korzhuev"]
10
+ s.email = ["andrew@korzhuev.com"]
11
+ s.homepage = "https://github.com/andrusha/multi_case"
12
+ s.summary = %q{Multi-parameter case statement}
13
+ s.description = %q{A state-machine inspired case/if statement}
14
+
15
+ s.rubyforge_project = "multi_case"
16
+
17
+ s.files = `git ls-files -z`.split("\0")
18
+ s.test_files = `git ls-files -z -- {fixtures,features,spec}/*`.split("\0")
19
+ s.extra_rdoc_files = ["LICENSE", "README.md"]
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_development_dependency "rake"
23
+ s.add_development_dependency "rspec"
24
+ s.add_development_dependency "simplecov"
25
+ end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ describe MultiCase::API do
4
+ include MultiCase::API
5
+
6
+ describe "simple matching" do
7
+ it "matches two argument against pre-defined cases returning specified value" do
8
+ result = multi_case :a, :c do
9
+ multi(:a => :b) { 1 }
10
+ multi(:a => :c) { 2 }
11
+ end
12
+
13
+ result.should == 2
14
+ end
15
+
16
+ it "return nil if no matches found" do
17
+ result = multi_case :x, :y do
18
+ multi(:a => :b) { 1 }
19
+ multi(:a => :c) { 2 }
20
+ end
21
+
22
+ result.should be_nil
23
+ end
24
+
25
+ it "returns first match if we have multiple matches" do
26
+ result = multi_case :x, :y do
27
+ multi(:a => :b) { 1 }
28
+ multi(:x => :y) { 2 }
29
+ multi(:x => [:y, :z]) { 3 }
30
+ end
31
+
32
+ result.should == 2
33
+ end
34
+
35
+ it "doesn't execute unmatched functions" do
36
+ expect do
37
+ result = multi_case :a, :c do
38
+ multi(:a => :b) { raise "oh shit" }
39
+ multi(:a => :c) { 2 }
40
+ multi(:a => :c) { raise "oh shittiest" }
41
+ end
42
+ end.not_to raise_error
43
+ end
44
+ end
45
+
46
+ describe "case matching" do
47
+ it "classes" do
48
+ result = multi_case 'aaa', :y do
49
+ multi(:a => :b) { 1 }
50
+ multi(String => :y) { 2 }
51
+ end
52
+
53
+ result.should == 2
54
+ end
55
+
56
+ it "regexps" do
57
+ result = multi_case 'aaa', :y do
58
+ multi(:a => :b) { 1 }
59
+ multi(/A+/i => :y) { 2 }
60
+ end
61
+
62
+ result.should == 2
63
+ end
64
+
65
+ it "ranges" do
66
+ result = multi_case 'aaa', 100 do
67
+ multi(:a => :b) { 1 }
68
+ multi('aaa' => :c) { 2 }
69
+ multi('aaa' => [:c, 99..120]) { 3 }
70
+ end
71
+
72
+ result.should == 3
73
+ end
74
+ end
75
+
76
+ describe "array matching" do
77
+ it "allow arbitrary input" do
78
+ result = multi_case :a, :c do
79
+ multi([:c, :a] => [:b, :d]) { 1 }
80
+ multi([:c, :d] => [:c, :e]) { 2 }
81
+ multi([:c, :a] => [:c, :e]) { 3 }
82
+ end
83
+
84
+ result.should == 3
85
+ end
86
+
87
+ it "treat empty arrays as catch-all" do
88
+ result = multi_case :a, :c do
89
+ multi([] => [:c]) { 1 }
90
+ end
91
+
92
+ result.should == 1
93
+ end
94
+
95
+ it "can match nested arrays" do
96
+ result = multi_case [:a, :b], :c do
97
+ multi([[:a, :b]] => [:c]) { 1 }
98
+ end
99
+
100
+ result.should == 1
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'multi_case/core_ext'
3
+
4
+ def f
5
+ multi_case :a, :b do
6
+ multi(:a => :b) { 1 }
7
+ end
8
+ end
9
+
10
+ describe "core extensions" do
11
+ it "makes multi_case global function" do
12
+ value = multi_case :a, :b do
13
+ multi(:a => :b) { 1 }
14
+ end
15
+
16
+ value.should == 1
17
+ end
18
+
19
+ it "allow you to call it from inside arbitrary function" do
20
+ f.should == 1
21
+ end
22
+
23
+ it "is accessible from inside another class" do
24
+ Class.new do
25
+ def f
26
+ multi_case :a, :b do
27
+ multi(:a => :b) { 1 }
28
+ end
29
+ end
30
+ end.new.f.should == 1
31
+ end
32
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec'
2
+ require 'multi_case'
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multi_case
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrey Korzhuev
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: simplecov
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A state-machine inspired case/if statement
63
+ email:
64
+ - andrew@korzhuev.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files:
68
+ - LICENSE
69
+ - README.md
70
+ files:
71
+ - .gitignore
72
+ - .rspec
73
+ - Gemfile
74
+ - LICENSE
75
+ - README.md
76
+ - Rakefile
77
+ - lib/multi_case.rb
78
+ - lib/multi_case/api.rb
79
+ - lib/multi_case/core_ext.rb
80
+ - lib/multi_case/dsl.rb
81
+ - multi_case.gemspec
82
+ - spec/multi_case/api_spec.rb
83
+ - spec/multi_case/core_ext_spec.rb
84
+ - spec/spec_helper.rb
85
+ - travis.yml
86
+ homepage: https://github.com/andrusha/multi_case
87
+ licenses: []
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project: multi_case
106
+ rubygems_version: 1.8.23
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Multi-parameter case statement
110
+ test_files:
111
+ - spec/multi_case/api_spec.rb
112
+ - spec/multi_case/core_ext_spec.rb
113
+ - spec/spec_helper.rb