salestation 0.2.1 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fab51d38331535ba91704962caed90aafe63239b
4
- data.tar.gz: f9a1cab5d37fb3af72a9db99f42d8021ed10211a
3
+ metadata.gz: f1b525e1ec3e904fe1121233b9c9334348df43d0
4
+ data.tar.gz: 8b74d25e23e0c52c214591b55ad8a347765e2b32
5
5
  SHA512:
6
- metadata.gz: d3a2ded9db7798961555d9cac8c478e09d701c60bd4a7ed66a56771c88a4989b7c16a7091a1c162f2ee117ac29408561be2552f6424e450c0f4f0dc1bbaa8562
7
- data.tar.gz: c48aa6527625c1635c9746e9a7c5201d3b31d46cdeeb323e3c88fa6bccb0258f25e2bb1c7e8a0028203d37ff3c23e1b185a26376d73127124e58df9b9768463a
6
+ metadata.gz: 4c90e2e8ca7df677c87888b70788c0ac1337e78beb4f777a4825d2a57d6cd5167a19e9e80b383732ba8a390400bc2127050c67a8429adf9d1c2d2dd90d5b84f3
7
+ data.tar.gz: 5a50bc06f24eda87d84955e84eeffc253c87b33729b5c3cdb797f0ac521037278ccb949e976f06bd2e5134adae4b9e99af52fee26e5afc9a10614d2f5adf0e70
data/README.md CHANGED
@@ -75,6 +75,50 @@ Salestation allows and recommends you to define your own custom errors. This is
75
75
  })
76
76
  ```
77
77
 
78
+ ### Using Extractors
79
+
80
+ Salestation provides extractors to fetch parameters from the request and pass them to the chain.
81
+ Available extractors are `BodyParamExtractor`, `QueryParamExtractor`, `ConstantInput`, `HeadersExtractor`.
82
+ Multiple extractors can be merged together. If two or more extractors use the same key, the value will be from the last extractor in the merge chain.
83
+
84
+ `coercions` can optionally be provided to `BodyParamExtractor` and `QueryParamExtractor`. These can be used to transform the values of the extracted parameters.
85
+
86
+ Define a route
87
+
88
+ ```ruby
89
+ include Salestation::Web::Extractors
90
+
91
+ post '/hello/:name' do |name|
92
+ coercions = {age: ->(age) { age.to_s }}
93
+
94
+ extractor = BodyParamExtractor[:age, coercions: coercions]
95
+ .merge(ConstantInput[name: name])
96
+ .merge(HeadersExtractor[{'authorization' => :auth}])
97
+
98
+ process extractor do |request|
99
+ HelloUser.call(request)
100
+ .map(Responses.to_ok)
101
+ end
102
+ end
103
+
104
+ ERROR_MAPPER = Salestation::Web::ErrorMapper.new
105
+
106
+ def process(extract_input, &process_request)
107
+ response = extract_input.call(request).match do
108
+ Success() do |value|
109
+ create_request(value)
110
+ .map(process_request)
111
+ .map_err(&ERROR_MAPPER.map)
112
+ end
113
+ Failure() do |value|
114
+ Result::Success(value)
115
+ end
116
+ end
117
+ rescue Salestation::Web::ErrorMapper::UndefinedErrorClass => exception
118
+ raise exception
119
+ end
120
+ ```
121
+
78
122
  ### Using a logger
79
123
 
80
124
  Salestation provides a rack logging middleware which can be used to log structured objects.
@@ -30,6 +30,7 @@ module Salestation
30
30
  end
31
31
  end
32
32
 
33
+ require_relative './web/extractors'
33
34
  require_relative './web/responses'
34
35
  require_relative './web/error_mapper'
35
36
  require_relative './result_helper'
@@ -0,0 +1,103 @@
1
+ module Salestation
2
+ class Web < Module
3
+ module Extractors
4
+ class InputExtractor
5
+ include Deterministic
6
+
7
+ def initialize(&block)
8
+ @block = block
9
+ end
10
+
11
+ def call(rack_request)
12
+ @block.call(rack_request)
13
+ end
14
+
15
+ def merge(other)
16
+ CombinedInputExtractor.new([self, other])
17
+ end
18
+ end
19
+
20
+ class CombinedInputExtractor
21
+ def initialize(extractors)
22
+ @extractors = extractors
23
+ end
24
+
25
+ def compose_seq(fns, input)
26
+ fns.reduce(Deterministic::Result::Success({})) do |result, fn|
27
+ result.map do |previous_value|
28
+ fn.call(input).map do |new_value|
29
+ Deterministic::Result::Success(yield(previous_value, new_value))
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def call(rack_request)
36
+ compose_seq(@extractors, rack_request) do |previous_input, new_input|
37
+ previous_input.merge(new_input)
38
+ end
39
+ end
40
+
41
+ def merge(other_extractor)
42
+ CombinedInputExtractor.new(@extractors + [other_extractor])
43
+ end
44
+ end
45
+
46
+ class HeadersExtractor
47
+ include Deterministic
48
+
49
+ def self.[](headers)
50
+ InputExtractor.new do |rack_request|
51
+ input = headers.map do |header, key|
52
+ value = rack_request.env["HTTP_#{header.upcase.tr('-', '_')}"]
53
+ next if value.nil?
54
+ [key, value]
55
+ end.compact.to_h
56
+
57
+ Result::Success(input)
58
+ end
59
+ end
60
+ end
61
+
62
+ class ParamExtractor
63
+ include Deterministic
64
+
65
+ def self.[](*keys, coercions: {}, rack_key:)
66
+ InputExtractor.new do |rack_request|
67
+ request_hash = rack_request.env[rack_key] || {}
68
+
69
+ input = keys
70
+ .select { |key| request_hash.key?(key.to_s) }
71
+ .map { |key| [key, request_hash[key.to_s]] }
72
+ .map { |key, value| coercions.key?(key) ? [key, coercions[key].call(value)] : [key, value] }
73
+ .to_h
74
+
75
+ Result::Success(input)
76
+ end
77
+ end
78
+ end
79
+
80
+ class QueryParamExtractor
81
+ def self.[](*keys, coercions: {})
82
+ ParamExtractor[*keys, coercions: coercions, rack_key: 'rack.request.query_hash']
83
+ end
84
+ end
85
+
86
+ class BodyParamExtractor
87
+ def self.[](*keys, coercions: {})
88
+ ParamExtractor[*keys, coercions: coercions, rack_key: 'rack.request.form_hash']
89
+ end
90
+ end
91
+
92
+ class ConstantInput
93
+ include Deterministic
94
+
95
+ def self.[](input)
96
+ InputExtractor.new do |**|
97
+ Result::Success(input)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
data/salestation.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "salestation"
7
- spec.version = "0.2.1"
7
+ spec.version = "0.3.0"
8
8
  spec.authors = ["SaleMove TechMovers"]
9
9
  spec.email = ["techmovers@salemove.com"]
10
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: salestation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SaleMove TechMovers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-26 00:00:00.000000000 Z
11
+ date: 2017-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -131,6 +131,7 @@ files:
131
131
  - lib/salestation/web.rb
132
132
  - lib/salestation/web/active_record_connection_management.rb
133
133
  - lib/salestation/web/error_mapper.rb
134
+ - lib/salestation/web/extractors.rb
134
135
  - lib/salestation/web/request_logger.rb
135
136
  - lib/salestation/web/responses.rb
136
137
  - lib/salestation/web/statsd_middleware.rb
@@ -155,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
156
  version: '0'
156
157
  requirements: []
157
158
  rubyforge_project:
158
- rubygems_version: 2.6.8
159
+ rubygems_version: 2.6.11
159
160
  signing_key:
160
161
  specification_version: 4
161
162
  summary: ''