salestation 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 91cc1ea2c779abccc3963933610f22c95318855d
4
+ data.tar.gz: e939be79576d14892348daf92e63d3295609c0ed
5
+ SHA512:
6
+ metadata.gz: e2089647f52e855db70555c8369dc59fd8ea5a840cdf1de86ba479b5dba5985017ea5c9fa1c7441aa26afdfd7f7be91efe2c9539c3aaaeb044a4e1c9e114922a
7
+ data.tar.gz: 9b0c99f5c46212feb7b8af2fcc86f1329671ec8d4da6cf130047e1e31b201f202445b271d9b927f9bb9c0ac7ecfc1cadde0df74109ce2e7a8c04d847837b9212
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.4
5
+ before_install: gem install bundler -v 1.13.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in salestation.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Indrek Juhkam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # Salestation
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/salestation`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'salestation'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install salestation
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/salestation.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,5 @@
1
+ require_relative './salestation/app'
2
+ require_relative './salestation/web'
3
+
4
+ module Salestation
5
+ end
@@ -0,0 +1,12 @@
1
+ require 'deterministic'
2
+ require 'virtus'
3
+
4
+ require_relative './app/errors'
5
+ require_relative './app/request'
6
+ require_relative './app/input_verification'
7
+ require_relative './app/result_helper'
8
+
9
+ module Salestation
10
+ module App
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ module Salestation
2
+ module App
3
+ module Errors
4
+ class InvalidInput
5
+ include Virtus.value_object(strict: true)
6
+
7
+ values do
8
+ attribute :errors, Hash
9
+ attribute :hints, Hash
10
+ end
11
+ end
12
+
13
+ class DependencyCurrentlyUnavailable
14
+ include Virtus.value_object(strict: true)
15
+
16
+ values do
17
+ attribute :message, String
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ module Salestation
2
+ module App
3
+ module InputVerification
4
+ def verify_input(schema)
5
+ -> (request) do
6
+ result = schema.call(request.input)
7
+ if result.success?
8
+ request.replace_input(result.output)
9
+ else
10
+ Deterministic::Result::Failure(
11
+ Errors::InvalidInput.new(errors: result.errors, hints: result.hints)
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ module Salestation
2
+ module App
3
+ class Request
4
+ def self.create(env:, input:)
5
+ new(env: env, input: input).to_success
6
+ end
7
+
8
+ attr_reader :env, :input
9
+
10
+ def with_input(input_additions)
11
+ replace_input(input.merge(input_additions))
12
+ end
13
+
14
+ def replace_input(new_input)
15
+ self.class.new(env: env, input: new_input).to_success
16
+ end
17
+
18
+ def to_success
19
+ Deterministic::Result::Success(self)
20
+ end
21
+
22
+ def to_failure(input)
23
+ Deterministic::Result::Failure(input)
24
+ end
25
+
26
+ private
27
+
28
+ def initialize(env:, input:)
29
+ @env = env
30
+ @input = input
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ module Salestation
2
+ module App
3
+ module ResultHelper
4
+ def observe(&block)
5
+ -> (result_value) do
6
+ block.call(result_value)
7
+ Deterministic::Result::Success(result_value)
8
+ end
9
+ end
10
+
11
+ def empty_success_response
12
+ -> (request) do
13
+ Deterministic::Result::Success({})
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ require_relative './web/responses'
2
+ require_relative './web/error_mapper'
3
+
4
+ module Salestation
5
+ module Web
6
+ ERROR_MAPPER = {
7
+ App::Errors::InvalidInput => -> (error) {
8
+ Responses::UnprocessableEntityFromSchemaErrors.create(error)
9
+ },
10
+ App::Errors::DependencyCurrentlyUnavailable => -> (error) {
11
+ Responses::ServiceUnavailable.new(
12
+ message: error.message,
13
+ debug_message: "Please try again later"
14
+ )
15
+ }
16
+ }.freeze
17
+
18
+ def process(response)
19
+ result = response.map_err(&ErrorMapper.map).value
20
+
21
+ status result.status
22
+ json JSON.dump(result.body)
23
+ end
24
+
25
+ def create_request(env, input)
26
+ App::Request.create(env: env, input: input)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module Salestation
2
+ module Web
3
+ module ErrorMapper
4
+ ERROR_TO_RESPONSE = {
5
+ App::Errors::InvalidInput => -> (error) {
6
+ Responses::UnprocessableEntityFromSchemaErrors.create(error)
7
+ },
8
+ App::Errors::DependencyCurrentlyUnavailable => -> (error) {
9
+ Responses::ServiceUnavailable.new(
10
+ message: error.message,
11
+ debug_message: "Please try again later"
12
+ )
13
+ }
14
+ }.freeze
15
+
16
+ def self.map
17
+ -> (error) do
18
+ _, error_mapper = ERROR_TO_RESPONSE
19
+ .find {|error_type, _| error.kind_of?(error_type) }
20
+
21
+ # Interpret a Failure from the application layer as Success in the web
22
+ # layer. Even if the domain specific operation failed, the web layer is
23
+ # still able to successfully produce a well-formed response.
24
+ if error_mapper
25
+ Deterministic::Result::Success(error_mapper.call(error))
26
+ elsif error.is_a?(Hash)
27
+ Deterministic::Result::Success(Responses::InternalError.new(error))
28
+ else
29
+ raise "Unknown error #{error.class.name}"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,73 @@
1
+ module Salestation
2
+ module Web
3
+ module Responses
4
+ def self.to_created
5
+ -> (object) { Deterministic::Result::Success(Responses::Created.new(body: object)) }
6
+ end
7
+
8
+ def self.to_accepted
9
+ -> (object) { Deterministic::Result::Success(Responses::Accepted.new(body: object)) }
10
+ end
11
+
12
+ module Response
13
+ def with_code(code)
14
+ Class.new(self) do
15
+ define_method :initialize do |attrs|
16
+ super(attrs.merge(status: code))
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ class Error
23
+ extend Response
24
+ include Virtus.value_object(strict: true)
25
+
26
+ values do
27
+ attribute :status, Integer
28
+ attribute :message, String
29
+ attribute :debug_message, String, default: ''
30
+ attribute :context, Hash, default: {}
31
+ end
32
+
33
+ def body
34
+ {message: message}
35
+ end
36
+ end
37
+
38
+ class Success
39
+ extend Response
40
+ include Virtus.value_object(strict: true)
41
+
42
+ values do
43
+ attribute :status, Integer
44
+ attribute :body, Hash
45
+ end
46
+ end
47
+
48
+ class UnprocessableEntityFromSchemaErrors
49
+ def self.create(errors:, hints:)
50
+ message = errors
51
+ .map { |field, error_messages| "'#{field}' #{error_messages.join(' and ')}" }
52
+ .join(". ")
53
+
54
+ debug_message = hints
55
+ .select {|field, hint_messages| hint_messages.any? }
56
+ .map { |field, hint_messages| "'#{field}' #{hint_messages.join(' and ')}" }
57
+ .join(". ")
58
+
59
+ UnprocessableEntity.new(message: message, debug_message: debug_message)
60
+ end
61
+ end
62
+
63
+ Created = Success.with_code(201)
64
+ Accepted = Success.with_code(202)
65
+
66
+ Unauthorized = Error.with_code(401)
67
+ UnprocessableEntity = Error.with_code(422)
68
+
69
+ InternalError = Error.with_code(500)
70
+ ServiceUnavailable = Error.with_code(503)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "salestation"
7
+ spec.version = "0.0.1"
8
+ spec.authors = ["SaleMove Developers"]
9
+ spec.email = ["support@salemove.com"]
10
+
11
+ spec.summary = %q{}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.13"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+
26
+ spec.add_dependency 'deterministic'
27
+ spec.add_dependency 'virtus'
28
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: salestation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - SaleMove Developers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: deterministic
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: virtus
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: ''
84
+ email:
85
+ - support@salemove.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/salestation.rb
98
+ - lib/salestation/app.rb
99
+ - lib/salestation/app/errors.rb
100
+ - lib/salestation/app/input_verification.rb
101
+ - lib/salestation/app/request.rb
102
+ - lib/salestation/app/result_helper.rb
103
+ - lib/salestation/web.rb
104
+ - lib/salestation/web/error_mapper.rb
105
+ - lib/salestation/web/responses.rb
106
+ - salestation.gemspec
107
+ homepage: ''
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.5.1
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: ''
131
+ test_files: []