algebrick 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -3
  3. data/README_FULL.md +13 -21
  4. data/VERSION +1 -1
  5. data/doc/actor.rb +21 -0
  6. data/doc/data.in.rb +99 -0
  7. data/doc/data.out.rb +103 -0
  8. data/doc/extending_behavior.in.rb +61 -0
  9. data/doc/extending_behavior.out.rb +62 -0
  10. data/doc/format.rb +75 -0
  11. data/doc/init.rb +1 -0
  12. data/doc/json.in.rb +39 -0
  13. data/doc/json.out.rb +43 -0
  14. data/doc/null.in.rb +36 -0
  15. data/doc/null.out.rb +40 -0
  16. data/doc/parametrized.in.rb +37 -0
  17. data/doc/parametrized.out.rb +41 -0
  18. data/doc/pattern_matching.in.rb +116 -0
  19. data/doc/pattern_matching.out.rb +122 -0
  20. data/doc/quick_example.in.rb +27 -0
  21. data/doc/quick_example.out.rb +27 -0
  22. data/doc/tree1.in.rb +10 -0
  23. data/doc/tree1.out.rb +10 -0
  24. data/doc/type_def.in.rb +21 -0
  25. data/doc/type_def.out.rb +21 -0
  26. data/doc/values.in.rb +52 -0
  27. data/doc/values.out.rb +58 -0
  28. data/lib/algebrick/atom.rb +49 -0
  29. data/lib/algebrick/dsl.rb +104 -0
  30. data/lib/algebrick/field_method_readers.rb +43 -0
  31. data/lib/algebrick/matcher_delegations.rb +45 -0
  32. data/lib/algebrick/matchers/abstract.rb +127 -0
  33. data/lib/algebrick/matchers/abstract_logic.rb +38 -0
  34. data/lib/algebrick/matchers/and.rb +29 -0
  35. data/lib/algebrick/matchers/any.rb +37 -0
  36. data/lib/algebrick/matchers/array.rb +57 -0
  37. data/lib/algebrick/matchers/atom.rb +28 -0
  38. data/lib/algebrick/matchers/not.rb +44 -0
  39. data/lib/algebrick/matchers/or.rb +51 -0
  40. data/lib/algebrick/matchers/product.rb +73 -0
  41. data/lib/algebrick/matchers/variant.rb +29 -0
  42. data/lib/algebrick/matchers/wrapper.rb +57 -0
  43. data/lib/algebrick/matchers.rb +31 -0
  44. data/lib/algebrick/matching.rb +62 -0
  45. data/lib/algebrick/parametrized_type.rb +122 -0
  46. data/lib/algebrick/product_constructors/abstract.rb +70 -0
  47. data/lib/algebrick/product_constructors/basic.rb +47 -0
  48. data/lib/algebrick/product_constructors/named.rb +58 -0
  49. data/lib/algebrick/product_constructors.rb +25 -0
  50. data/lib/algebrick/product_variant.rb +195 -0
  51. data/lib/algebrick/reclude.rb +39 -0
  52. data/lib/algebrick/serializer.rb +129 -0
  53. data/lib/algebrick/serializers.rb +25 -0
  54. data/lib/algebrick/type.rb +61 -0
  55. data/lib/algebrick/type_check.rb +58 -0
  56. data/lib/algebrick/types.rb +59 -0
  57. data/lib/algebrick/value.rb +41 -0
  58. data/lib/algebrick.rb +14 -1170
  59. data/spec/algebrick_test.rb +708 -0
  60. metadata +105 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d3e1690d46e7e9d3c9332eed298a46d35ec628ce
4
- data.tar.gz: 7cf4ccef5ae69a055e9ac7806b84e0a6724d7c1b
3
+ metadata.gz: df5b658188ef94835725f056d1872942a1f10fe3
4
+ data.tar.gz: 5ac342bdfaaa354e8bf9575c25f1e06105170d18
5
5
  SHA512:
6
- metadata.gz: ae2f4b9cd4bebbb889b8d91383cbe9380d8b2c440104c0302b4b128b589fbdb6d1bdae886a49cf790022a76f1fe304180bc8b6ed77da6ce60d6aba038fac67fd
7
- data.tar.gz: becadc540cc08d5002060e0fc1e64eac87d1848ff11bc44b6e628a45b61bc705fcce870741e568b2e6803e39b08b7f789d42e2926d176f8c40c8083bea3db21c
6
+ metadata.gz: 1207c3040b5d32c3d64cc47c29f9bccb83a979600cc9dc169d1424b6514297bc3c844bfa12e5d851663f08382019cdf948a85397e4363f21b4f835756c92235a
7
+ data.tar.gz: af70a94f628feb94b59995adc5d6740cafdddf655a4c560f25e788c31f758138e21a6617abff047fc7d195176cf5357b7e9a65decb25916c0176d240b0f0eeb8
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/pitr-ch/algebrick.png?branch=master)](https://travis-ci.org/pitr-ch/algebrick)
4
4
 
5
- It's a gem providing **algebraic types** and **pattern matching** seamlessly integrates with standard features Ruby.
5
+ Typed structs on steroids based on algebraic types and pattern matching seamlessly integrating with standard Ruby features.
6
6
 
7
7
  - Documentation: <http://blog.pitr.ch/algebrick>
8
8
  - Source: <https://github.com/pitr-ch/algebrick>
@@ -10,8 +10,11 @@ It's a gem providing **algebraic types** and **pattern matching** seamlessly int
10
10
 
11
11
  ## What is it good for?
12
12
 
13
- - Defining data structures.
14
- - Algebraic types play nice with JSON serialization and deserialization. It is ideal for defining message-based cross-process communication.
13
+ - Well defined data structures.
14
+ - Actor messages see [new Actor implementation](http://rubydoc.info/gems/concurrent-ruby/Concurrent/Actress)
15
+ in [concurrent-ruby](concurrent-ruby.com).
16
+ - Describing message protocols in message-based cross-process communication.
17
+ Algebraic types play nice with JSON de/serialization.
15
18
  - and more...
16
19
 
17
20
  ## Quick example
data/README_FULL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Algebrick
2
2
 
3
- It's a gem providing **algebraic types** and **pattern matching** and seamlessly integrating with standard features of Ruby.
3
+ Typed structs on steroids based on algebraic types and pattern matching seamlessly integrating with standard Ruby features.
4
4
 
5
5
  - Documentation: <http://blog.pitr.ch/algebrick>
6
6
  - Source: <https://github.com/pitr-ch/algebrick>
@@ -12,19 +12,23 @@ It's a gem providing **algebraic types** and **pattern matching** and seamlessly
12
12
 
13
13
  ## Algebraic types
14
14
 
15
- Algebraic type is a kind of composite type, i.e. a type formed by combining other types.
15
+ Algebraic types are:
16
+
17
+ - products with a given number of fields,
18
+ - or a kind of composite type, i.e. a type formed by combining other types.
19
+
16
20
  In Haskell algebraic type looks like this:
17
21
 
18
22
  data Tree = Empty
19
23
  | Leaf Int
20
24
  | Node Tree Tree
21
-
22
- depth :: Tree -> Int
23
- depth Empty = 0
24
- depth (Leaf n) = 1
25
- depth (Node l r) = 1 + max (depth l) (depth r)
25
+
26
+ depth :: Tree -> Int
27
+ depth Empty = 0
28
+ depth (Leaf n) = 1
29
+ depth (Node l r) = 1 + max (depth l) (depth r)
26
30
 
27
- depth (Node Empty (Leaf 5)) -- => 2
31
+ depth (Node Empty (Leaf 5)) -- => 2
28
32
 
29
33
  This snippet defines type `Tree` which has 3 possible values:
30
34
 
@@ -95,19 +99,7 @@ Algebraic types also play nice with JSON serialization and de-serialization maki
95
99
 
96
100
  Just a small snippet how it can be used in Actor model world.
97
101
 
98
- class Worker < AbstractActor
99
- def initialize(executor)
100
- super()
101
- @executor = executor
102
- end
103
-
104
- def on_message(message)
105
- match message,
106
- Work.(~any, ~any) >-> actor, work do
107
- @executor.tell Finished[actor, work.call, self.reference]
108
- end
109
- end
110
- end
102
+ {include:file:doc/actor.rb}
111
103
 
112
104
  <!--
113
105
  ### Null Object Pattern
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.5.0
data/doc/actor.rb ADDED
@@ -0,0 +1,21 @@
1
+ Work = Algebrick.type do
2
+ fields key: String, work: Proc
3
+ end
4
+
5
+ Finished = Algebrick.type do
6
+ fields key: String, result: Object, worker: Worker
7
+ end
8
+
9
+ class Worker < AbstractActor
10
+ def initialize(executor)
11
+ super()
12
+ @executor = executor
13
+ end
14
+
15
+ def on_message(message)
16
+ match message,
17
+ Work.(~any, ~any) >-> key, work do
18
+ @executor.tell Finished[key, work.call, self]
19
+ end
20
+ end
21
+ end
data/doc/data.in.rb ADDED
@@ -0,0 +1,99 @@
1
+ extend Algebrick::Matching
2
+
3
+ # Simple data structures like trees
4
+ Tree = Algebrick.type do |tree|
5
+ variants Tip = type,
6
+ Node = type { fields value: Object, left: tree, right: tree }
7
+ end
8
+
9
+ module Tree
10
+ def depth
11
+ match self,
12
+ Tip.to_m >> 0,
13
+ Node.(any, ~any, ~any) >-> left, right do
14
+ 1 + [left.depth, right.depth].max
15
+ end
16
+ end
17
+ end #
18
+
19
+ tree = Node[2,
20
+ Tip,
21
+ Node[5,
22
+ Node[4, Tip, Tip],
23
+ Node[6, Tip, Tip]]]
24
+ tree.depth
25
+
26
+ # Whenever you find yourself to pass around too many fragile Hash-Array structures
27
+ # e.g. for menus.
28
+ Menu = Algebrick.type do |menu|
29
+ Item = Algebrick.type do
30
+ variants Delimiter = atom,
31
+ Link = type { fields! label: String, url: String },
32
+ Group = type { fields! label: String, submenu: menu }
33
+ end
34
+
35
+ fields! item: Item, next: menu
36
+ variants None = atom, menu
37
+ end
38
+ None
39
+ Item
40
+
41
+ module Link
42
+ def self.new(*fields)
43
+ super(*fields).tap { |menu| valid! menu.url }
44
+ end
45
+
46
+ def self.valid!(url)
47
+ # stub
48
+ end
49
+ end #
50
+
51
+ module Item
52
+ def draw_menu(indent = 0)
53
+ match self,
54
+ Delimiter >-> { [' '*indent + '-'*10] },
55
+ Link.(label: ~any) >-> label { [' '*indent + label] },
56
+ (on ~Group do |(label, sub_menu)|
57
+ [' '*indent + label] + sub_menu.draw_menu(indent + 2)
58
+ end)
59
+ end
60
+ end #
61
+
62
+ module Menu
63
+ def self.build(*items)
64
+ items.reverse_each.reduce(None) { |menu, item| Menu[item, menu] }
65
+ end
66
+
67
+ include Enumerable
68
+
69
+ def each(&block)
70
+ it = self
71
+ loop do
72
+ break if None === it
73
+ block.call it.item
74
+ it = it.next
75
+ end
76
+ end
77
+
78
+ def draw_menu(indent = 0)
79
+ map { |item| item.draw_menu indent }.reduce(&:+)
80
+ end
81
+ end #
82
+
83
+ sub_menu = Menu.build Link['Red Hat', '#red-hat'],
84
+ Delimiter,
85
+ Link['Ubuntu', '#ubuntu'],
86
+ Link['Mint', '#mint']
87
+
88
+ menu = Menu.build Link['Home', '#home'],
89
+ Delimiter,
90
+ Group['Linux', sub_menu],
91
+ Link['About', '#about']
92
+
93
+ menu.draw_menu.join("\n")
94
+
95
+
96
+ # Group['Products',
97
+ # Menu[]],
98
+ # Link['About', '#about']
99
+ #]]
data/doc/data.out.rb ADDED
@@ -0,0 +1,103 @@
1
+ extend Algebrick::Matching # => main
2
+
3
+ # Simple data structures like trees
4
+ Tree = Algebrick.type do |tree|
5
+ variants Tip = type,
6
+ Node = type { fields value: Object, left: tree, right: tree }
7
+ end # => Tree(Tip | Node)
8
+
9
+ module Tree
10
+ def depth
11
+ match self,
12
+ Tip.to_m >> 0,
13
+ Node.(any, ~any, ~any) >-> left, right do
14
+ 1 + [left.depth, right.depth].max
15
+ end
16
+ end
17
+ end
18
+
19
+ tree = Node[2,
20
+ Tip,
21
+ Node[5,
22
+ Node[4, Tip, Tip],
23
+ Node[6, Tip, Tip]]]
24
+ # => Node[value: 2, left: Tip, right: Node[value: 5, left: Node[value: 4, left: Tip, right: Tip], right: Node[value: 6, left: Tip, right: Tip]]]
25
+ tree.depth # => 3
26
+
27
+ # Whenever you find yourself to pass around too many fragile Hash-Array structures
28
+ # e.g. for menus.
29
+ Menu = Algebrick.type do |menu|
30
+ Item = Algebrick.type do
31
+ variants Delimiter = atom,
32
+ Link = type { fields! label: String, url: String },
33
+ Group = type { fields! label: String, submenu: menu }
34
+ end
35
+
36
+ fields! item: Item, next: menu
37
+ variants None = atom, menu
38
+ end # => Menu(None | Menu(item: Item, next: Menu))
39
+ None # => None
40
+ Item # => Item(Delimiter | Link | Group)
41
+
42
+ module Link
43
+ def self.new(*fields)
44
+ super(*fields).tap { |menu| valid! menu.url }
45
+ end
46
+
47
+ def self.valid!(url)
48
+ # stub
49
+ end
50
+ end
51
+
52
+ module Item
53
+ def draw_menu(indent = 0)
54
+ match self,
55
+ Delimiter >-> { [' '*indent + '-'*10] },
56
+ Link.(label: ~any) >-> label { [' '*indent + label] },
57
+ (on ~Group do |(label, sub_menu)|
58
+ [' '*indent + label] + sub_menu.draw_menu(indent + 2)
59
+ end)
60
+ end
61
+ end
62
+
63
+ module Menu
64
+ def self.build(*items)
65
+ items.reverse_each.reduce(None) { |menu, item| Menu[item, menu] }
66
+ end
67
+
68
+ include Enumerable
69
+
70
+ def each(&block)
71
+ it = self
72
+ loop do
73
+ break if None === it
74
+ block.call it.item
75
+ it = it.next
76
+ end
77
+ end
78
+
79
+ def draw_menu(indent = 0)
80
+ map { |item| item.draw_menu indent }.reduce(&:+)
81
+ end
82
+ end
83
+
84
+ sub_menu = Menu.build Link['Red Hat', '#red-hat'],
85
+ Delimiter,
86
+ Link['Ubuntu', '#ubuntu'],
87
+ Link['Mint', '#mint']
88
+ # => Menu[item: Link[label: Red Hat, url: #red-hat], next: Menu[item: Delimiter, next: Menu[item: Link[label: Ubuntu, url: #ubuntu], next: Menu[item: Link[label: Mint, url: #mint], next: None]]]]
89
+
90
+ menu = Menu.build Link['Home', '#home'],
91
+ Delimiter,
92
+ Group['Linux', sub_menu],
93
+ Link['About', '#about']
94
+ # => Menu[item: Link[label: Home, url: #home], next: Menu[item: Delimiter, next: Menu[item: Group[label: Linux, submenu: Menu[item: Link[label: Red Hat, url: #red-hat], next: Menu[item: Delimiter, next: Menu[item: Link[label: Ubuntu, url: #ubuntu], next: Menu[item: Link[label: Mint, url: #mint], next: None]]]]], next: Menu[item: Link[label: About, url: #about], next: None]]]]
95
+
96
+ menu.draw_menu.join("\n")
97
+ # => "Home\n----------\nLinux\n Red Hat\n ----------\n Ubuntu\n Mint\nAbout"
98
+
99
+
100
+ # Group['Products',
101
+ # Menu[]],
102
+ # Link['About', '#about']
103
+ #]]
@@ -0,0 +1,61 @@
1
+ Maybe = Algebrick.type do
2
+ variants None = atom,
3
+ Some = type { fields Object }
4
+ end
5
+
6
+ # Types can be extended with usual syntax for modules and using Ruby supports module reopening.
7
+ module Maybe
8
+ def maybe(&block)
9
+ case self
10
+ when None
11
+ when Some
12
+ block.call value
13
+ end
14
+ end
15
+ end #
16
+
17
+ # #maybe method is defined on both values (None, Some) of Maybe.
18
+ None.maybe { |_| raise 'never ever happens' }
19
+ # Block is called with the value.
20
+ Some[1].maybe { |v| v*2 }
21
+
22
+ # It also works as expected when modules like Comparable are included.
23
+ Season = Algebrick.type do
24
+ variants Spring = atom,
25
+ Summer = atom,
26
+ Autumn = atom,
27
+ Winter = atom
28
+ end
29
+
30
+ module Season
31
+ include Comparable
32
+ ORDER = Season.variants.each_with_index.each_with_object({}) { |(season, i), h| h[season] = i }
33
+
34
+ def <=>(other)
35
+ Type! other, Season
36
+ ORDER[self] <=> ORDER[other]
37
+ end
38
+ end #
39
+
40
+ Quarter = Algebrick.type do
41
+ fields! year: Integer, season: Season
42
+ end
43
+
44
+ module Quarter
45
+ include Comparable
46
+
47
+ def <=>(other)
48
+ Type! other, Quarter
49
+ [year, season] <=> [other.year, other.season]
50
+ end
51
+ end #
52
+
53
+ # Now Quarters and Seasons can be compared as expected.
54
+ [Winter, Summer, Spring, Autumn].sort
55
+ Quarter[2013, Spring] < Quarter[2013, Summer]
56
+ Quarter[2014, Spring] > Quarter[2013, Summer]
57
+ Quarter[2014, Spring] == Quarter[2014, Spring]
58
+ [Quarter[2013, Spring], Quarter[2013, Summer], Quarter[2014, Spring]].sort
59
+
60
+
61
+
@@ -0,0 +1,62 @@
1
+ Maybe = Algebrick.type do
2
+ variants None = atom,
3
+ Some = type { fields Object }
4
+ end # => Maybe(None | Some)
5
+
6
+ # Types can be extended with usual syntax for modules and using Ruby supports module reopening.
7
+ module Maybe
8
+ def maybe(&block)
9
+ case self
10
+ when None
11
+ when Some
12
+ block.call value
13
+ end
14
+ end
15
+ end
16
+
17
+ # #maybe method is defined on both values (None, Some) of Maybe.
18
+ None.maybe { |_| raise 'never ever happens' } # => nil
19
+ # Block is called with the value.
20
+ Some[1].maybe { |v| v*2 } # => 2
21
+
22
+ # It also works as expected when modules like Comparable are included.
23
+ Season = Algebrick.type do
24
+ variants Spring = atom,
25
+ Summer = atom,
26
+ Autumn = atom,
27
+ Winter = atom
28
+ end # => Season(Spring | Summer | Autumn | Winter)
29
+
30
+ module Season
31
+ include Comparable
32
+ ORDER = Season.variants.each_with_index.each_with_object({}) { |(season, i), h| h[season] = i }
33
+
34
+ def <=>(other)
35
+ Type! other, Season
36
+ ORDER[self] <=> ORDER[other]
37
+ end
38
+ end
39
+
40
+ Quarter = Algebrick.type do
41
+ fields! year: Integer, season: Season
42
+ end # => Quarter(year: Integer, season: Season)
43
+
44
+ module Quarter
45
+ include Comparable
46
+
47
+ def <=>(other)
48
+ Type! other, Quarter
49
+ [year, season] <=> [other.year, other.season]
50
+ end
51
+ end
52
+
53
+ # Now Quarters and Seasons can be compared as expected.
54
+ [Winter, Summer, Spring, Autumn].sort # => [Spring, Summer, Autumn, Winter]
55
+ Quarter[2013, Spring] < Quarter[2013, Summer] # => true
56
+ Quarter[2014, Spring] > Quarter[2013, Summer] # => true
57
+ Quarter[2014, Spring] == Quarter[2014, Spring] # => true
58
+ [Quarter[2013, Spring], Quarter[2013, Summer], Quarter[2014, Spring]].sort
59
+ # => [Quarter[year: 2013, season: Spring], Quarter[year: 2013, season: Summer], Quarter[year: 2014, season: Spring]]
60
+
61
+
62
+
data/doc/format.rb ADDED
@@ -0,0 +1,75 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'pry'
4
+ require 'pp'
5
+
6
+ input_paths = if ARGV.empty?
7
+ Dir.glob("#{File.dirname(__FILE__)}/*.in.rb")
8
+ else
9
+ ARGV
10
+ end.map { |p| File.expand_path p }
11
+
12
+ input_paths.each_with_index do |input_path, i|
13
+
14
+ pid = fork do
15
+ require_relative 'init.rb'
16
+
17
+ begin
18
+ output_path = input_path.gsub /\.in\.rb$/, '.out.rb'
19
+ input = File.readlines(input_path)
20
+
21
+ chunks = []
22
+ line = ''
23
+
24
+ while !input.empty?
25
+ line += input.shift
26
+ if Pry::Code.complete_expression? line
27
+ chunks << line
28
+ line = ''
29
+ end
30
+ end
31
+
32
+ raise unless line.empty?
33
+
34
+ chunks.map! { |chunk| [chunk, [chunk.split($/).size, 1].max] }
35
+ environment = Module.new.send :binding
36
+ evaluate = ->(code, line) do
37
+ eval(code, environment, input_path, line)
38
+ end
39
+
40
+ indent = 50
41
+
42
+ line_count = 1
43
+ output = ''
44
+ chunks.each do |chunk, lines|
45
+ result = evaluate.(chunk, line_count)
46
+ unless chunk.strip.empty? || chunk =~ /\A *#/
47
+ pre_lines = chunk.lines.to_a
48
+ last_line = pre_lines.pop
49
+ output << pre_lines.join
50
+
51
+ if last_line =~ /\#$/
52
+ output << last_line.gsub(/\#$/, '')
53
+ else
54
+ if last_line.size < indent && result.inspect.size < indent
55
+ output << "%-#{indent}s %s" % [last_line.chomp, "# => #{result.inspect}\n"]
56
+ else
57
+ output << last_line << " # => #{result.inspect}\n"
58
+ end
59
+ end
60
+ else
61
+ output << chunk
62
+ end
63
+ line_count += lines
64
+ end
65
+
66
+ puts "#{input_path}\n -> #{output_path}"
67
+ #puts output
68
+ File.write(output_path, output)
69
+ rescue => ex
70
+ puts "#{ex} (#{ex.class})\n#{ex.backtrace * "\n"}"
71
+ end
72
+ end
73
+
74
+ Process.wait pid
75
+ end
data/doc/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'algebrick'
data/doc/json.in.rb ADDED
@@ -0,0 +1,39 @@
1
+ extend Algebrick::Matching
2
+
3
+ # Lets define message-protocol for a cross-process communication.
4
+ Request = Algebrick.type do
5
+ User = type { fields login: String, password: String }
6
+
7
+ variants CreateUser = type { fields User },
8
+ GetUser = type { fields login: String }
9
+ end
10
+
11
+ Response = Algebrick.type do
12
+ variants Success = type { fields Object },
13
+ Failure = type { fields error: String }
14
+ end
15
+
16
+ Message = Algebrick.type { variants Request, Response }
17
+
18
+ require 'algebrick/serializer'
19
+ require 'multi_json'
20
+
21
+ # Prepare a message for sending.
22
+ serializer = Algebrick::Serializer.new
23
+ request = CreateUser[User['root', 'lajDh4']]
24
+ raw_request = MultiJson.dump serializer.dump(request)
25
+
26
+ # Receive the message.
27
+ response = match serializer.load(MultiJson.load(raw_request)),
28
+ (on CreateUser.(~any) do |user|
29
+ # create the user and send success
30
+ Success[user]
31
+ end)
32
+
33
+ # Send response.
34
+ response_raw = MultiJson.dump serializer.dump(response)
35
+
36
+ # Receive response.
37
+ serializer.load(MultiJson.load(response_raw))
38
+
39
+
data/doc/json.out.rb ADDED
@@ -0,0 +1,43 @@
1
+ extend Algebrick::Matching # => main
2
+
3
+ # Lets define message-protocol for a cross-process communication.
4
+ Request = Algebrick.type do
5
+ User = type { fields login: String, password: String }
6
+
7
+ variants CreateUser = type { fields User },
8
+ GetUser = type { fields login: String }
9
+ end # => Request(CreateUser | GetUser)
10
+
11
+ Response = Algebrick.type do
12
+ variants Success = type { fields Object },
13
+ Failure = type { fields error: String }
14
+ end # => Response(Success | Failure)
15
+
16
+ Message = Algebrick.type { variants Request, Response }
17
+ # => Message(Request | Response)
18
+
19
+ require 'algebrick/serializer' # => true
20
+ require 'multi_json' # => true
21
+
22
+ # Prepare a message for sending.
23
+ serializer = Algebrick::Serializer.new # => #<Algebrick::Serializer:0x007fab42bdf6d8>
24
+ request = CreateUser[User['root', 'lajDh4']]
25
+ # => CreateUser[User[login: root, password: lajDh4]]
26
+ raw_request = MultiJson.dump serializer.dump(request)
27
+ # => "{\"algebrick_type\":\"CreateUser\",\"algebrick_fields\":[{\"algebrick_type\":\"User\",\"login\":\"root\",\"password\":\"lajDh4\"}]}"
28
+
29
+ # Receive the message.
30
+ response = match serializer.load(MultiJson.load(raw_request)),
31
+ (on CreateUser.(~any) do |user|
32
+ # create the user and send success
33
+ Success[user]
34
+ end) # => Success[User[login: root, password: lajDh4]]
35
+
36
+ # Send response.
37
+ response_raw = MultiJson.dump serializer.dump(response)
38
+ # => "{\"algebrick_type\":\"Success\",\"algebrick_fields\":[{\"algebrick_type\":\"User\",\"login\":\"root\",\"password\":\"lajDh4\"}]}"
39
+
40
+ # Receive response.
41
+ serializer.load(MultiJson.load(response_raw)) # => Success[User[login: root, password: lajDh4]]
42
+
43
+
data/doc/null.in.rb ADDED
@@ -0,0 +1,36 @@
1
+ extend Algebrick::Matching
2
+
3
+ def deliver_email(email)
4
+ true
5
+ end #
6
+
7
+ Contact = Algebrick.type do |contact|
8
+ variants Null = atom,
9
+ contact
10
+ fields username: String, email: String
11
+ end
12
+
13
+ module Contact
14
+ def username
15
+ match self,
16
+ Null >> 'no name',
17
+ Contact >-> { self[:username] }
18
+ end
19
+ def email
20
+ match self,
21
+ Null >> 'no email',
22
+ Contact >-> { self[:email] }
23
+ end
24
+ def deliver_personalized_email
25
+ match self,
26
+ Null >> true,
27
+ Contact >-> { deliver_email(self.email) }
28
+ end
29
+ end #
30
+
31
+ peter = Contact['peter', 'example@dot.com']
32
+ john = Contact[username: 'peter', email: 'example@dot.com']
33
+ nobody = Null
34
+
35
+ [peter, john, nobody].map &:email
36
+ [peter, john, nobody].map &:deliver_personalized_email
data/doc/null.out.rb ADDED
@@ -0,0 +1,40 @@
1
+ extend Algebrick::Matching # => main
2
+
3
+ def deliver_email(email)
4
+ true
5
+ end
6
+
7
+ Contact = Algebrick.type do |contact|
8
+ variants Null = atom,
9
+ contact
10
+ fields username: String, email: String
11
+ end
12
+ # => Contact(Null | Contact(username: String, email: String))
13
+
14
+ module Contact
15
+ def username
16
+ match self,
17
+ Null >> 'no name',
18
+ Contact >-> { self[:username] }
19
+ end
20
+ def email
21
+ match self,
22
+ Null >> 'no email',
23
+ Contact >-> { self[:email] }
24
+ end
25
+ def deliver_personalized_email
26
+ match self,
27
+ Null >> true,
28
+ Contact >-> { deliver_email(self.email) }
29
+ end
30
+ end
31
+
32
+ peter = Contact['peter', 'example@dot.com'] # => Contact[username: peter, email: example@dot.com]
33
+ john = Contact[username: 'peter', email: 'example@dot.com']
34
+ # => Contact[username: peter, email: example@dot.com]
35
+ nobody = Null # => Null
36
+
37
+ [peter, john, nobody].map &:email
38
+ # => ["example@dot.com", "example@dot.com", "no email"]
39
+ [peter, john, nobody].map &:deliver_personalized_email
40
+ # => [true, true, true]