multi_case 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.
@@ -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