vodka 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +14 -0
  3. data/Gemfile +4 -0
  4. data/LICENCE +13 -0
  5. data/README.md +119 -0
  6. data/lib/party.rb +9 -0
  7. data/lib/vodka/client/exceptions/exception.rb +6 -0
  8. data/lib/vodka/client/exceptions/failed_action_exception.rb +6 -0
  9. data/lib/vodka/client/exceptions/forbidden_exception.rb +11 -0
  10. data/lib/vodka/client/exceptions/not_found_exception.rb +11 -0
  11. data/lib/vodka/client/exceptions/resource_exception.rb +6 -0
  12. data/lib/vodka/client/middleware/error_aware.rb +21 -0
  13. data/lib/vodka/client/middleware/signed_request.rb +34 -0
  14. data/lib/vodka/client.rb +16 -0
  15. data/lib/vodka/configuration.rb +25 -0
  16. data/lib/vodka/her/extensions/extended_orm.rb +92 -0
  17. data/lib/vodka/her/extensions/will_paginate.rb +32 -0
  18. data/lib/vodka/server/controllers/vodka_controller.rb +32 -0
  19. data/lib/vodka/server/handlers/resource.rb +36 -0
  20. data/lib/vodka/server/handlers/response.rb +34 -0
  21. data/lib/vodka/server/handlers/scaffold.rb +63 -0
  22. data/lib/vodka/server/middleware/signed_request.rb +39 -0
  23. data/lib/vodka/server/plugins/presentable.rb +20 -0
  24. data/lib/vodka/server/railtie.rb +11 -0
  25. data/lib/vodka/server/response.rb +34 -0
  26. data/lib/vodka/server.rb +15 -0
  27. data/lib/vodka/version.rb +3 -0
  28. data/lib/vodka.rb +5 -0
  29. data/script/bundle_dummy +4 -0
  30. data/script/ci +4 -0
  31. data/script/setup_db +7 -0
  32. data/script/show_logs +22 -0
  33. data/script/spec +14 -0
  34. data/script/start_server +6 -0
  35. data/script/stop_server +6 -0
  36. data/spec/client/article.rb +4 -0
  37. data/spec/dummy/Gemfile +9 -0
  38. data/spec/dummy/Gemfile.lock +113 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  41. data/spec/dummy/app/controllers/vodka/articles_controller.rb +8 -0
  42. data/spec/dummy/app/models/article.rb +10 -0
  43. data/spec/dummy/config/application.rb +64 -0
  44. data/spec/dummy/config/boot.rb +6 -0
  45. data/spec/dummy/config/database.yml +17 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +32 -0
  48. data/spec/dummy/config/environments/production.rb +54 -0
  49. data/spec/dummy/config/environments/test.rb +37 -0
  50. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  51. data/spec/dummy/config/initializers/inflections.rb +15 -0
  52. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  53. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  54. data/spec/dummy/config/initializers/session_store.rb +8 -0
  55. data/spec/dummy/config/initializers/vodka_setup.rb +3 -0
  56. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/spec/dummy/config/locales/en.yml +2 -0
  58. data/spec/dummy/config/locales/ru.yml +2 -0
  59. data/spec/dummy/config/routes.rb +7 -0
  60. data/spec/dummy/config/thin.yml +10 -0
  61. data/spec/dummy/config.ru +4 -0
  62. data/spec/dummy/db/migrate/20130217185353_create_articles.rb +9 -0
  63. data/spec/dummy/db/schema.rb +22 -0
  64. data/spec/dummy/db/seeds.rb +1 -0
  65. data/spec/dummy/log/.gitkeep +0 -0
  66. data/spec/dummy/script/rails +6 -0
  67. data/spec/her/extensions/extended_orm/create_spec.rb +33 -0
  68. data/spec/her/extensions/extended_orm/delete_spec.rb +23 -0
  69. data/spec/her/extensions/extended_orm/destroy_spec.rb +35 -0
  70. data/spec/her/extensions/extended_orm/first_spec.rb +11 -0
  71. data/spec/her/extensions/extended_orm/last_spec.rb +11 -0
  72. data/spec/her/extensions/extended_orm/update_attribute_spec.rb +44 -0
  73. data/spec/her/extensions/extended_orm/update_attributes_spec.rb +44 -0
  74. data/spec/her/extensions/extended_orm/where_spec.rb +39 -0
  75. data/spec/her/extensions/will_paginate/paginate_spec.rb +16 -0
  76. data/spec/middleware/error_aware_spec.rb +19 -0
  77. data/spec/middleware/response_locale_spec.rb +12 -0
  78. data/spec/middleware/signed_request_spec.rb +9 -0
  79. data/spec/spec_helper.rb +24 -0
  80. data/vodka.gemspec +21 -0
  81. metadata +190 -0
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ /*.gem
2
+ /.bundle
3
+ /Gemfile.lock
4
+
5
+ /spec/dummy/db/*.sqlite3
6
+ /spec/dummy/log/*
7
+ /spec/dummy/tmp/pids/*
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ rvm:
2
+ - 1.9.3
3
+ script: ./script/ci
4
+ before_script:
5
+ - ./script/bundle_dummy
6
+ - ./script/setup_db
7
+ - ./script/start_server
8
+ after_script:
9
+ - ./script/stop_server
10
+ after_failure:
11
+ - ./script/show_logs
12
+ notifications:
13
+ hipchat:
14
+ secure: "SUoVBD+3cNXuzHK/xiww4e2jWdo9GHz+YE09s+z0K51nPtlTy9NFaBF41O7W\nMc4sz0ZxktnVrpVvyOGmLj933uyyRjhfK9X3R6Fn/BMzWDI1S4Z2NqhKq3xy\nGpLjEAX8UPtWXR0Cs4GxTwxTM6rjIW/I/5IpM6hiwRErSBXBin0="
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vodka.gemspec
4
+ gemspec
data/LICENCE ADDED
@@ -0,0 +1,13 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENCE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Vodka
2
+ Vodka makes communication easier. Always.
3
+
4
+ Vodka uses [Her](https://github.com/remiprev/her) as a REST client.
5
+
6
+ It currently supports ORM's on server:
7
+ - [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord)
8
+ - [MongoMapper](https://github.com/jnunemaker/mongomapper)
9
+
10
+ Plugins:
11
+ - [WillPaginate](https://github.com/mislav/will_paginate)
12
+
13
+ It is strongly recommended *NOT* to use this gem in production (yet).
14
+
15
+ ## Installation
16
+ Add this gem to both server and client application Gemfiles:
17
+ ```ruby
18
+ # Server
19
+ gem 'vodka', require: 'vodka/server'
20
+
21
+ # Client
22
+ gem 'vodka', require: 'vodka/client'
23
+ ```
24
+
25
+ ## Configuring server
26
+ Add initializer `vodka_setup.rb` to `config/initializers`:
27
+
28
+ ```ruby
29
+ Vodka::Server.configure do |c|
30
+ c.secret = 'whatever'
31
+ end
32
+ ```
33
+
34
+ Add vodka namespaced resources to your `config/routes.rb`
35
+ ```ruby
36
+ namespace :vodka do
37
+ resources :articles do
38
+ collection { get :best }
39
+ resources :comments do
40
+ member { put :approve }
41
+ end
42
+ end
43
+ end
44
+ ```
45
+
46
+ Create controllers in `app/controllers/vodka`
47
+ ```ruby
48
+ # articles_controller.rb
49
+ class ArticlesController < VodkaController
50
+ def best
51
+ respond_with_collection(Article.best)
52
+ end
53
+ end
54
+
55
+ # comments_controller.rb
56
+ class CommentsController < Vodka::Server::VodkaController
57
+ def approve
58
+ vodka_response.success = resource.approve
59
+ respond_with_resource
60
+ end
61
+ end
62
+ ```
63
+
64
+ Modify your models:
65
+ ```ruby
66
+ # article.rb
67
+ class Article < ActiveRecord::Base
68
+ has_many :comments
69
+ scope :best, ->{ where('rating > 100') }
70
+ validates_presence_of :title, :body
71
+
72
+ # Defines fields and actions that would be used in :as_json method
73
+ present_with :id, :title, :body, :created_at
74
+ end
75
+
76
+ # comment.rb
77
+ class Comment < ActiveRecord::Base
78
+ belongs_to :article
79
+ belongs_to :user
80
+ validates_presence_of :body
81
+
82
+ # Defines fields and actions that would be used in :as_json method
83
+ present_with :id, :body, :author_name, :created_at
84
+
85
+ def approve
86
+ update_attributes(status: 'approved')
87
+ end
88
+
89
+ def author_name
90
+ [user.first_name, user.last_name].join(' ')
91
+ end
92
+ end
93
+ ```
94
+
95
+ ## Configuring client
96
+ Add initializer `vodka_setup.rb` to `config/initializers`:
97
+
98
+ ```ruby
99
+ Vodka::Client.configure do |c|
100
+ c.api_url = 'https://api.myproject.org/vodka'
101
+ c.secret = 'whatever' # Same as server's
102
+ end
103
+ Vodka::Client.configure_her!
104
+ ```
105
+
106
+ ## Usage
107
+ After all the configuration is done, you can use your Her-applied models with all the new possibilities.
108
+
109
+ Vodka adds some convinient methods to client and supports them on server:
110
+ - `.create!` (throws exception on error)
111
+ - `.paginate` (same as `.all`, for WillPaginate compatibility)
112
+ - `.where` (supports chaining the way you expect)
113
+ - `#update_attribute`
114
+ - `#update_attribute!` (throws exception on error)
115
+ - `#update_attributes`
116
+ - `#update_attributes!` (throws exception on error)
117
+ - `#destroy!` (throws exception on error)
118
+ - `#delete` (acts as `#destroy`)
119
+ - `#delete!` (acts as `#destroy!`)
data/lib/party.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'i18n'
2
+ require 'vodka/client'
3
+ require './spec/client/article'
4
+
5
+ Vodka::Client.configure do |c|
6
+ c.api_url = 'http://0.0.0.0:3000/vodka'
7
+ c.secret = 'whatever'
8
+ end
9
+ Vodka::Client.configure_her!
@@ -0,0 +1,6 @@
1
+ module Vodka
2
+ module Client
3
+ class Exception < ::Exception
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Vodka
2
+ module Client
3
+ class FailedActionException < ::Exception
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module Vodka
2
+ module Client
3
+ class ForbiddenException < ::Exception
4
+ MESSAGE = '403 Forbidden'
5
+
6
+ def initialize
7
+ super(MESSAGE)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Vodka
2
+ module Client
3
+ class NotFoundException < ::Exception
4
+ MESSAGE = '404 Not Found'
5
+
6
+ def initialize
7
+ super(MESSAGE)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module Vodka
2
+ module Client
3
+ class ResourceException < ::Exception
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module Vodka
2
+ module Client
3
+ module Middleware
4
+ class ErrorAware < Faraday::Response::Middleware
5
+ def on_complete(env)
6
+ env[:body] = MultiJson.load(env[:body]) unless env[:body].is_a?(Hash)
7
+ return unless env[:body][:errors].present? && env[:body][:errors][:vodka_error].present?
8
+
9
+ case env[:body][:errors][:vodka_error]
10
+ when ForbiddenException::MESSAGE
11
+ raise ForbiddenException.new
12
+ when NotFoundException::MESSAGE
13
+ raise NotFoundException.new
14
+ else
15
+ raise Exception.new(env[:body][:errors][:vodka_error])
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ module Vodka
2
+ module Client
3
+ module Middleware
4
+ class SignedRequest < Faraday::Middleware
5
+ attr_reader :app, :env
6
+
7
+ def initialize(app, options = {})
8
+ @app = app
9
+ end
10
+
11
+ def call(_env)
12
+ @env = _env
13
+
14
+ env[:request_headers]['X-Request-Id'] = request_id
15
+ env[:request_headers]['X-Request-Signature'] = request_signature
16
+ env[:request_headers]['X-Response-Locale'] = I18n.locale.to_s if defined?(I18n)
17
+
18
+ @app.call(env)
19
+ end
20
+
21
+ def request_id
22
+ @request_id ||= [Time.now.to_i, (rand * 100_000_000).to_i].join(?_)
23
+ end
24
+
25
+ def request_signature
26
+ Digest::SHA512.hexdigest([
27
+ env[:url].scheme, env[:url].host, env[:url].port, env[:url].path,
28
+ request_id, Vodka::Client.config.secret
29
+ ].join)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ require 'vodka'
2
+ require 'vodka/client/exceptions/exception'
3
+ require 'vodka/client/exceptions/failed_action_exception'
4
+ require 'vodka/client/exceptions/forbidden_exception'
5
+ require 'vodka/client/exceptions/not_found_exception'
6
+ require 'vodka/client/exceptions/resource_exception'
7
+ require 'vodka/client/middleware/signed_request'
8
+ require 'vodka/client/middleware/error_aware'
9
+ require 'vodka/her/extensions/extended_orm'
10
+ require 'vodka/her/extensions/will_paginate'
11
+
12
+ module Vodka
13
+ module Client
14
+ extend Configurable
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module Vodka
2
+ class Configuration
3
+ attr_accessor :secret, :api_url
4
+ end
5
+
6
+ module Configurable
7
+ def config
8
+ @config ||= Configuration.new
9
+ end
10
+
11
+ def configure
12
+ yield config if block_given?
13
+ end
14
+
15
+ def configure_her!
16
+ ::Her::API.setup(url: Vodka::Client.config.api_url) do |c|
17
+ c.use Vodka::Client::Middleware::ErrorAware
18
+ c.use Vodka::Client::Middleware::SignedRequest
19
+ c.use Faraday::Request::UrlEncoded
20
+ c.use ::Her::Middleware::SecondLevelParseJSON
21
+ c.use Faraday::Adapter::NetHttp
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,92 @@
1
+ module Vodka
2
+ module Her
3
+ module Extensions
4
+ module ExtendedOrm
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def create!(*args)
9
+ resource = create(*args)
10
+ unless resource.errors.empty?
11
+ error = [resource.errors.keys.first, resource.errors.values.first].join(' ')
12
+ raise Vodka::Client::ResourceException.new(error)
13
+ end
14
+
15
+ resource
16
+ end
17
+
18
+ def first
19
+ resources = all(vodka_special_action: 'first')
20
+ resource = resources.first
21
+ resource.errors = resources.errors
22
+ resource.metadata = resources.metadata
23
+ resource
24
+ end
25
+
26
+ def last
27
+ resources = all(vodka_special_action: 'last')
28
+ resource = resources.first
29
+ resource.errors = resources.errors
30
+ resource.metadata = resources.metadata
31
+ resource
32
+ end
33
+
34
+ def where(*args)
35
+ @where_conditions ||= []
36
+ @where_conditions << args
37
+ self
38
+ end
39
+
40
+ def all(params = {})
41
+ unless @where_conditions.nil?
42
+ params[:vodka_special_where] = MultiJson.dump(@where_conditions)
43
+ @where_conditions = nil
44
+ end
45
+ super(params)
46
+ end
47
+ end
48
+
49
+ def update_attributes(params)
50
+ params.each do |attribute, value|
51
+ send(:"#{attribute}=", value)
52
+ end
53
+ save
54
+ end
55
+
56
+ def update_attributes!(params)
57
+ update_attributes(params)
58
+ unless errors.empty?
59
+ error = [errors.keys.first, errors.values.first].join(' ')
60
+ raise Vodka::Client::ResourceException.new(error)
61
+ end
62
+
63
+ self
64
+ end
65
+
66
+ def update_attribute(attribute, value)
67
+ update_attributes(attribute => value)
68
+ end
69
+
70
+ def update_attribute!(attribute, value)
71
+ update_attributes!(attribute => value)
72
+ end
73
+
74
+ def destroy!
75
+ destroy
76
+ raise Vodka::Client::FailedActionException.new('Destroy failed') if metadata[:vodka_action_success] == false
77
+ self
78
+ end
79
+
80
+ def delete
81
+ destroy
82
+ end
83
+
84
+ def delete!
85
+ destroy!
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ Her::Model.send(:include, Vodka::Her::Extensions::ExtendedOrm)
@@ -0,0 +1,32 @@
1
+ module Vodka
2
+ module Her
3
+ module Extensions
4
+ module WillPaginate
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def paginate(params)
9
+ response = all(params)
10
+ PaginatedCollection.new(response, response.metadata, response.errors)
11
+ end
12
+ end
13
+ end
14
+
15
+ class PaginatedCollection < ::Her::Collection
16
+ def current_page
17
+ metadata[:page].to_i
18
+ end
19
+
20
+ def per_page
21
+ metadata[:per_page].to_i
22
+ end
23
+
24
+ def total_entries
25
+ metadata[:total].to_i
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ Her::Model.send(:include, Vodka::Her::Extensions::WillPaginate)
@@ -0,0 +1,32 @@
1
+ require 'action_controller' unless defined?(::ActionController)
2
+
3
+ module Vodka
4
+ module Server
5
+ class VodkaController < ::ActionController::Base
6
+ include Handlers::Scaffold
7
+ include Handlers::Resource
8
+ include Handlers::Response
9
+
10
+ before_filter :fix_params_names, :set_locale
11
+ before_filter :handle_not_found, only: [:show, :update, :destroy]
12
+
13
+ private
14
+
15
+ def set_locale
16
+ return unless defined?(I18n)
17
+ locale = request.headers['X-Response-Locale']
18
+ I18n.locale = locale if locale.present? && locale.to_sym.in?(I18n.available_locales)
19
+ end
20
+
21
+ def fix_params_names
22
+ params[:password] = params.delete(:buzzword) unless params[:buzzword].nil?
23
+ end
24
+
25
+ def handle_not_found
26
+ not_found if resource.nil? && vodka_response.success == false
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ VodkaController = Vodka::Server::VodkaController
@@ -0,0 +1,36 @@
1
+ module Vodka
2
+ module Server
3
+ module Handlers
4
+ module Resource
5
+ private
6
+
7
+ def resource
8
+ @resource ||= extract_resource
9
+ end
10
+
11
+ def extract_resource
12
+ return unless params.has_key?(:id)
13
+
14
+ new_resource = resource_class.find_by_id(params[:id])
15
+ vodka_response.success = false if new_resource.nil?
16
+
17
+ new_resource
18
+ end
19
+
20
+ def filtered_params
21
+ params.slice(*resource_class.accessible_attributes.to_a)
22
+ end
23
+
24
+ # Resource magick
25
+
26
+ def resource_name
27
+ self.class.name.demodulize.underscore.gsub('_controller', '').singularize
28
+ end
29
+
30
+ def resource_class
31
+ resource_name.camelcase.constantize
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ module Vodka
2
+ module Server
3
+ module Handlers
4
+ module Response
5
+
6
+ def vodka_response
7
+ @vodka_response ||= Vodka::Server::Response.new
8
+ end
9
+
10
+ private
11
+
12
+ def respond_with_resource(custom_resource = nil)
13
+ vodka_response.data = custom_resource || resource
14
+ respond!
15
+ end
16
+
17
+ def respond_with_collection(resources)
18
+ vodka_response.data = resources
19
+ respond!
20
+ end
21
+
22
+ def respond!
23
+ return render text: vodka_response.json, status: vodka_response.code, content_type: 'application/json'
24
+ end
25
+
26
+ def not_found
27
+ vodka_response.errors[:vodka_error] = '404 Not Found'
28
+ vodka_response.code = 404
29
+ respond!
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,63 @@
1
+ module Vodka
2
+ module Server
3
+ module Handlers
4
+ module Scaffold
5
+ def index
6
+ if params[:vodka_special_action] == 'first'
7
+ resources = [resource_class.first]
8
+ elsif params[:vodka_special_action] == 'last'
9
+ resources = [resource_class.last]
10
+ elsif params[:vodka_special_where].present?
11
+ resources = vodka_special_where
12
+ elsif params[:page].present? && defined?(::WillPaginate)
13
+ resources = vodka_special_paginate
14
+ else
15
+ resources = resource_class.all
16
+ end
17
+
18
+ respond_with_collection(resources)
19
+ end
20
+
21
+ def show
22
+ respond_with_resource
23
+ end
24
+
25
+ def create
26
+ respond_with_resource(resource_class.create(filtered_params))
27
+ end
28
+
29
+ def update
30
+ vodka_response.success = resource.update_attributes(filtered_params)
31
+ respond_with_resource
32
+ end
33
+
34
+ def destroy
35
+ resource.destroy
36
+ vodka_response.success = resource.destroyed?
37
+ respond_with_resource
38
+ end
39
+
40
+ private
41
+
42
+ def vodka_special_where
43
+ relation = resource_class
44
+ conditions = MultiJson.load(params[:vodka_special_where])
45
+ conditions.each do |condition|
46
+ if condition.is_a?(Hash)
47
+ relation = relation.where(condition)
48
+ else
49
+ relation = relation.where(*condition)
50
+ end
51
+ end
52
+ relation.all
53
+ end
54
+
55
+ def vodka_special_paginate
56
+ data = resource_class.paginate(page: params[:page], per_page: params[:per_page]).all
57
+ vodka_response.metadata = { page: data.current_page, per_page: data.per_page, total: resource_class.count }
58
+ data
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,39 @@
1
+ module Vodka
2
+ module Server
3
+ module Middleware
4
+ class SignedRequest
5
+ attr_reader :app, :env, :request
6
+
7
+ def initialize(app, options = {})
8
+ @app, @options = app, options
9
+ end
10
+
11
+ def call(env)
12
+ @env = env
13
+ @request = Rack::Request.new(env)
14
+
15
+ request_signature_valid? ? app.call(env) : forbidden
16
+ end
17
+
18
+ def request_signature_valid?
19
+ env['HTTP_X_REQUEST_SIGNATURE'] == request_signature
20
+ end
21
+
22
+ def request_signature
23
+ Digest::SHA512.hexdigest([
24
+ request.scheme, request.host, request.port, request.path,
25
+ env['HTTP_X_REQUEST_ID'], Vodka::Server.config.secret
26
+ ].join)
27
+ end
28
+
29
+ def forbidden
30
+ [
31
+ 403,
32
+ { 'Content-Type' => 'application/json; charset=utf-8' },
33
+ ['{"data":null,"errors":{"vodka_error":"403 Forbidden"},"metadata":{}}']
34
+ ]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,20 @@
1
+ module Vodka
2
+ module Server
3
+ module Plugins
4
+ module Presentable
5
+ def present_with(*methods)
6
+ define_method 'present_vodka' do
7
+ json = Hash[methods.map{ |method| [method, send(method)] }]
8
+ json[:errors] = errors.messages
9
+ json[:metadata] = {}
10
+ json
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ ActiveRecord::Base.send(:extend, Vodka::Server::Plugins::Presentable) if defined?(ActiveRecord)
19
+ MongoMapper::Document.send(:extend, Vodka::Server::Plugins::Presentable) if defined?(MongoMapper)
20
+ MongoMapper::EmbeddedDocument.send(:extend, Vodka::Server::Plugins::Presentable) if defined?(MongoMapper)
@@ -0,0 +1,11 @@
1
+ require 'rails' unless defined?(::Rails)
2
+
3
+ module Vodka
4
+ module Server
5
+ class Railtie < ::Rails::Railtie
6
+ initializer 'vodka.inject_middleware' do |app|
7
+ app.config.middleware.use 'Vodka::Server::Middleware::SignedRequest'
8
+ end
9
+ end
10
+ end
11
+ end