algebrick 0.4.0 → 0.5.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.
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]