railway-rb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/railway.rb +121 -0
  3. metadata +43 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8698c98d0e081c760f0c652d7875bf1ad9050f36989de032187156e68c6c6a90
4
+ data.tar.gz: 79a12dd5fd91fb69afb56e10f737939659ea2a5937f3abc83debc8c9d5cc380c
5
+ SHA512:
6
+ metadata.gz: 859724aff46f8909025681cb3a48eb01b5f7543c5ec00ec736ac6aa07ed4709fe732e205e869a0e837bb828dbebf4c3e4aece4d0433b348f934cbe9328a41dd1
7
+ data.tar.gz: 6b2dd0ca7b4e48e692dd324958905818b53e22879dbbbea39ed6ebff8c0f9178af38af8dc06dffae239c9180541e284f887de28d5778933f2a3c6ad61073bbe2
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Include this module to enable Railway syntactic expression.
4
+ # Example:
5
+ #
6
+ # class Lorem
7
+ # include Railway
8
+ #
9
+ # def greeting(person)
10
+ # with { person }.as var(:pax).when { pax.is_a? String }
11
+ # .then { "Hello, #{pax}" }.as var(:message) do
12
+ # pp message
13
+ # end.run
14
+ # end
15
+ # end
16
+ #
17
+ module Railway
18
+ def var(sym, setter: nil)
19
+ block = BuildingBlock.new(name: sym, setter_name: setter)
20
+ Builder.new(block)
21
+ end
22
+
23
+ def with(&assign_block)
24
+ block = BuildingBlock.new(assign_block: assign_block)
25
+ Builder.new(block, self)
26
+ end
27
+
28
+ BuildingBlock = Struct.new(
29
+ :name, :setter_name, :assign_block, :guard_block, :extra_blocks,
30
+ keyword_init: true
31
+ )
32
+
33
+ # This class is used to combine and execute building block properly
34
+ #
35
+ class Builder
36
+ attr_reader :blocks
37
+
38
+ def initialize(starting_block, ctx = nil)
39
+ @blocks = [starting_block]
40
+ @context = ctx
41
+ end
42
+
43
+ def when(&block)
44
+ blocks.first.guard_block = block
45
+ self
46
+ end
47
+
48
+ def then(&block)
49
+ blocks.last.extra_blocks = [*blocks.last.extra_blocks, next_assign_block] if next_assign_block
50
+
51
+ @next_assign_block = block
52
+ self
53
+ end
54
+
55
+ def as(next_builder, &block) # rubocop:disable Metrics/MethodLength
56
+ if block
57
+ @accept_block = block
58
+ assign_block = blocks.first.assign_block
59
+ @blocks = next_builder.blocks.clone
60
+ blocks.first.assign_block = assign_block
61
+ else
62
+ next_blocks = next_builder.blocks.clone
63
+ next_blocks.first.assign_block = next_assign_block
64
+ @blocks += next_blocks
65
+ @next_assign_block = nil
66
+ end
67
+ self
68
+ end
69
+
70
+ def otherwise(&block)
71
+ @reject_block = block if block
72
+ self
73
+ end
74
+
75
+ def run
76
+ @result = Struct.new(*blocks.map(&:name)).new
77
+
78
+ blocks.each do |block|
79
+ apply_block(block)
80
+ return reject(block) unless continue_block?(block)
81
+
82
+ [*block.extra_blocks].each { |b| instance_exec(&b) }
83
+ end
84
+
85
+ instance_exec(&accept_block)
86
+ end
87
+
88
+ private
89
+
90
+ attr_reader :context, :next_assign_block, :result, :accept_block, :reject_block
91
+
92
+ def apply_block(block)
93
+ sym = block.name
94
+ result[sym] = instance_exec(&block.assign_block)
95
+ return if !context || !block.setter_name
96
+
97
+ context.instance_variable_set("@#{block.setter_name}".to_sym, result[sym])
98
+ end
99
+
100
+ def continue_block?(block)
101
+ !block.guard_block || instance_exec(&block.guard_block)
102
+ end
103
+
104
+ def reject(block)
105
+ sym = block.name
106
+ reject_block && instance_exec(sym, result[sym], &reject_block)
107
+ end
108
+
109
+ def method_missing(a_method, *args, &block)
110
+ return result[a_method] if result&.members&.include?(a_method)
111
+
112
+ return context.send(a_method, *args, &block) if context.respond_to?(a_method, true)
113
+
114
+ super(a_method, *args, &block)
115
+ end
116
+
117
+ def respond_to_missing?(a_method, *)
118
+ result&.members&.include?(a_method) || context.respond_to?(a_method)
119
+ end
120
+ end
121
+ end
metadata ADDED
@@ -0,0 +1,43 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: railway-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hoan Nguyen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-12-25 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A functional approach to error handling. Inspired by Elixir `with` statement.
14
+ email: hoan006@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/railway.rb
20
+ homepage: http://github.com/hoan006
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.4'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubygems_version: 3.1.2
40
+ signing_key:
41
+ specification_version: 4
42
+ summary: Railway-Oriented Programming!
43
+ test_files: []