whiskey 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +9 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +74 -0
  10. data/ROADMAP.md +13 -0
  11. data/Rakefile +24 -0
  12. data/bin/whiskey +17 -0
  13. data/lib/whiskey.rb +16 -0
  14. data/lib/whiskey/command.rb +30 -0
  15. data/lib/whiskey/command/build.rb +89 -0
  16. data/lib/whiskey/command/start_server.rb +15 -0
  17. data/lib/whiskey/command/templates/Gemfile +10 -0
  18. data/lib/whiskey/command/templates/LICENSE.txt +22 -0
  19. data/lib/whiskey/command/templates/Procfile +1 -0
  20. data/lib/whiskey/command/templates/Thorfile +4 -0
  21. data/lib/whiskey/command/templates/db/seed.rb +15 -0
  22. data/lib/whiskey/command/templates/gitignore +17 -0
  23. data/lib/whiskey/command/templates/lib/action.rb +3 -0
  24. data/lib/whiskey/command/templates/lib/controls.rb +7 -0
  25. data/lib/whiskey/command/templates/lib/controls/router_control.rb +19 -0
  26. data/lib/whiskey/command/templates/lib/controls/world_control.rb +6 -0
  27. data/lib/whiskey/command/templates/lib/example.rb +7 -0
  28. data/lib/whiskey/command/templates/lib/models.rb +3 -0
  29. data/lib/whiskey/command/templates/lib/models/account.rb +6 -0
  30. data/lib/whiskey/command/templates/lib/models/actor.rb +6 -0
  31. data/lib/whiskey/command/templates/lib/models/place.rb +6 -0
  32. data/lib/whiskey/command/templates/ruby-gemset +1 -0
  33. data/lib/whiskey/command/templates/ruby-version +1 -0
  34. data/lib/whiskey/command/templates/server.rb +20 -0
  35. data/lib/whiskey/server.rb +81 -0
  36. data/lib/whiskey/server/action.rb +22 -0
  37. data/lib/whiskey/server/configuration.rb +9 -0
  38. data/lib/whiskey/server/connection.rb +51 -0
  39. data/lib/whiskey/server/control.rb +33 -0
  40. data/lib/whiskey/server/cycle.rb +35 -0
  41. data/lib/whiskey/server/deserializer.rb +21 -0
  42. data/lib/whiskey/server/error.rb +79 -0
  43. data/lib/whiskey/server/handler.rb +17 -0
  44. data/lib/whiskey/server/interpretor.rb +32 -0
  45. data/lib/whiskey/server/receiver.rb +19 -0
  46. data/lib/whiskey/server/responder.rb +19 -0
  47. data/lib/whiskey/server/router.rb +45 -0
  48. data/lib/whiskey/server/serializer.rb +22 -0
  49. data/lib/whiskey/version.rb +3 -0
  50. data/spec/lib/command/build_spec.rb +5 -0
  51. data/spec/lib/command/start_server_spec.rb +5 -0
  52. data/spec/lib/command_spec.rb +5 -0
  53. data/spec/lib/server/configuration_spec.rb +5 -0
  54. data/spec/lib/server/connection_spec.rb +43 -0
  55. data/spec/lib/server/cycle_spec.rb +23 -0
  56. data/spec/lib/server/deserializer_spec.rb +11 -0
  57. data/spec/lib/server/error_spec.rb +5 -0
  58. data/spec/lib/server/handler_spec.rb +5 -0
  59. data/spec/lib/server/interpretor_spec.rb +5 -0
  60. data/spec/lib/server/receiver_spec.rb +5 -0
  61. data/spec/lib/server/responder_spec.rb +15 -0
  62. data/spec/lib/server/serializer_spec.rb +11 -0
  63. data/spec/lib/server_spec.rb +31 -0
  64. data/spec/spec_helper.rb +9 -0
  65. data/test/lib/whiskey/server/connection_test.rb +42 -0
  66. data/test/lib/whiskey/server/cycle_test.rb +16 -0
  67. data/test/lib/whiskey/server/handler_test.rb +18 -0
  68. data/whiskey.gemspec +39 -0
  69. metadata +383 -0
@@ -0,0 +1,3 @@
1
+ class Action < Whiskey::Server::Action
2
+
3
+ end
@@ -0,0 +1,7 @@
1
+ require_relative "controls/router_control"
2
+ require_relative "controls/world_control"
3
+
4
+ module Controls
5
+ include Whiskey::Server::Control
6
+
7
+ end
@@ -0,0 +1,19 @@
1
+ module Controls
2
+ module RouteControl
3
+ include Whiskey::Server::Control
4
+
5
+ class ListAction < Action
6
+ include RouteControl
7
+
8
+ def initialize(parameters)
9
+ @routes = Controls.controls.map(&:to_route)
10
+ end
11
+
12
+ def to_hash
13
+ {
14
+ routes: @routes
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ module Controls
2
+ module WorldControl
3
+ include Whiskey::Server::Control
4
+
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ require_relative "models"
2
+ require_relative "action"
3
+ require_relative "controls"
4
+
5
+ module <%= values.name.classify %>
6
+
7
+ end
@@ -0,0 +1,3 @@
1
+ require_relative "models/account"
2
+ require_relative "models/place"
3
+ require_relative "models/actor"
@@ -0,0 +1,6 @@
1
+ class Account < ActiveRecord::Base
2
+ has_many :characters
3
+
4
+ validate :name, present: true
5
+ validate :email, present: true, uniqueness: true
6
+ end
@@ -0,0 +1,6 @@
1
+ class Actor < ActiveRecord::Base
2
+ belongs_to :account
3
+
4
+ validate :name, present: true
5
+ validate :description, present: true
6
+ end
@@ -0,0 +1,6 @@
1
+ class Place < ActiveRecord::Base
2
+ has_many :characters
3
+
4
+ validate :name, present: true
5
+ validate :description, present: true
6
+ end
@@ -0,0 +1 @@
1
+ <%= values.name %>
@@ -0,0 +1 @@
1
+ <%= values.ruby %>
@@ -0,0 +1,20 @@
1
+ require "bundler"
2
+
3
+ if defined?(Bundler)
4
+ ENV["WHISKEY_ENVIRONMENT"] ||= "development"
5
+ Bundler.require
6
+ Bundler.require ENV["WHISKEY_ENVIRONMENT"].to_sym
7
+ end
8
+
9
+ require "whiskey/server"
10
+
11
+ unless $LOAD_PATH.include?(File.expand_path("../lib", __FILE__))
12
+ $:.unshift File.expand_path("../lib", __FILE__)
13
+ end
14
+
15
+ require "<%= values.name %>"
16
+
17
+ Whiskey::Server.configure do |config|
18
+ config.host = ENV["WHISKEY_HOST"] || ENV["HOST"] || "0.0.0.0"
19
+ config.port = ENV["WHISKEY_PORT"] || ENV["PORT"] || 9000
20
+ end
@@ -0,0 +1,81 @@
1
+ require "whiskey"
2
+ require "celluloid/io"
3
+ require_relative "server/configuration"
4
+ require_relative "server/handler"
5
+ require_relative "server/connection"
6
+ require_relative "server/cycle"
7
+ require_relative "server/receiver"
8
+ require_relative "server/deserializer"
9
+ require_relative "server/interpretor"
10
+ require_relative "server/responder"
11
+ require_relative "server/serializer"
12
+ require_relative "server/error"
13
+ require_relative "server/control"
14
+ require_relative "server/action"
15
+ require_relative "server/router"
16
+
17
+ module Whiskey
18
+ class Server
19
+ include Celluloid::IO
20
+
21
+ # This method is the start point for the server, allowing the user
22
+ # to pass in settings for the server. It can be done like this:
23
+ #
24
+ # Whiskey::Server.configure(host: "localhost", port: 4222)
25
+ #
26
+ # or with a block:
27
+ #
28
+ # Whiskey::Server.configure do |config|
29
+ # config.host = "localhost"
30
+ # config.port = 4222
31
+ # end
32
+ def self.configure(options = {}, &block)
33
+ @@configuration = Configuration.new(options)
34
+ instance_exec(@@configuration, &block) if block_given?
35
+ start
36
+ end
37
+
38
+ # This method starts the server according to the configuration object
39
+ # and then follows the instructions set by Celluloid-Io examples.
40
+ def self.start
41
+ supervise(@@configuration.host, @@configuration.port)
42
+ trap("INT") { supervisor.terminate; exit }
43
+ sleep
44
+ end
45
+
46
+ attr_reader :server
47
+
48
+ def initialize(host, port)
49
+ Whiskey.logger.info "Initializing a server at #{@@configuration}..."
50
+ @server = TCPServer.new(host, port)
51
+ Whiskey.logger.info "Successfully bound host and port!"
52
+ async.run
53
+ end
54
+
55
+ def run
56
+ loop { async.handle_connection server.accept }
57
+ end
58
+
59
+ def finalizer(callback)
60
+ server.close if server
61
+ end
62
+
63
+ def handle_connection(socket)
64
+ connection = Connection.new(socket)
65
+ Whiskey.logger.info "New connection created from #{connection.id}"
66
+ begin
67
+ Handler.new(connection).handle
68
+ rescue => connection_error
69
+ Whiskey.logger.error connection_error
70
+ ensure
71
+ # If the connection has been broken, this might not work: Errno::EPIPE
72
+ begin
73
+ socket.write Serializer.new(Error.new(:internal_server_error).to_hash).data
74
+ rescue => socket_error
75
+ Whiskey.logger.error socket_error
76
+ end
77
+ socket.close
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,22 @@
1
+ module Whiskey
2
+ class Server
3
+ class Action
4
+ def self.to_verb
5
+ case name.demodulize
6
+ when "ListAction" then "PULL"
7
+ when "CreateAction" then "PUSH"
8
+ when "ShowAction" then "PULL"
9
+ when "UpdateAction" then "PUSH"
10
+ end
11
+ end
12
+
13
+ def initialize(*args)
14
+ raise "Not yet implemented"
15
+ end
16
+
17
+ def to_hash
18
+ raise "Not yet implemented"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ module Whiskey
2
+ class Server
3
+ class Configuration < AltStruct
4
+ def to_s
5
+ "#{host}:#{port}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,51 @@
1
+ module Whiskey
2
+ class Server
3
+ class Connection
4
+ extend Forwardable
5
+
6
+ attr_reader :socket
7
+
8
+ def initialize(socket)
9
+ @socket = socket
10
+ end
11
+
12
+ def id
13
+ "#{ip}:#{port}"
14
+ end
15
+
16
+ def process
17
+ write cycle
18
+ end
19
+
20
+ private
21
+
22
+ def cycle
23
+ Cycle.new(incoming).tap(&:interpret!).output
24
+ end
25
+
26
+ def incoming
27
+ readpartial(4096)
28
+ end
29
+
30
+ def port
31
+ peeraddr[1]
32
+ end
33
+
34
+ def ip
35
+ peeraddr[3]
36
+ end
37
+
38
+ def write(*args)
39
+ socket.write(*args)
40
+ end
41
+
42
+ def readpartial(*args)
43
+ socket.readpartial(*args)
44
+ end
45
+
46
+ def peeraddr(*args)
47
+ socket.peeraddr(*args)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,33 @@
1
+ module Whiskey
2
+ class Server
3
+ module Control
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def to_resource
10
+ name.demodulize.downcase.gsub(/control/, '')
11
+ end
12
+
13
+ def to_route
14
+ {
15
+ to_resource => actions.map(&:to_verb)
16
+ }
17
+ end
18
+
19
+ def controls
20
+ constants.select do |constant|
21
+ constant.to_s.end_with?("Control")
22
+ end.map(&method(:const_get))
23
+ end
24
+
25
+ def actions
26
+ constants.select do |constant|
27
+ constant.to_s.end_with?("Action")
28
+ end.map(&method(:const_get))
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ module Whiskey
2
+ class Server
3
+ class Cycle
4
+ attr_reader :input
5
+ attr_reader :response
6
+
7
+ def initialize(input)
8
+ @input = deserialized(input)
9
+ end
10
+
11
+ def interpret!
12
+ @response ||= Interpretor.new(input).response
13
+ end
14
+
15
+ def output
16
+ serialized(response)
17
+ end
18
+
19
+ private
20
+
21
+ def deserialized(input)
22
+ receiver = Receiver.new(input)
23
+ if receiver.valid?
24
+ receiver.deserialize
25
+ else
26
+ @response = Error.new(:bad_request).to_hash
27
+ end
28
+ end
29
+
30
+ def serialized(response)
31
+ Responder.new(response).serialize
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module Whiskey
2
+ class Server
3
+ class Deserializer
4
+ attr_reader :data
5
+
6
+ def initialize(raw)
7
+ begin
8
+ @data = MultiJson.load(raw.chomp!)
9
+ @valid = true
10
+ rescue Exception => error
11
+ Whiskey.logger.error error
12
+ @valid = false
13
+ end
14
+ end
15
+
16
+ def valid?
17
+ @valid
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,79 @@
1
+ module Whiskey
2
+ class Server
3
+ class Error
4
+ def initialize(code)
5
+ @payload = send(code)
6
+ Whiskey.logger.error(to_s)
7
+ end
8
+
9
+ def to_hash
10
+ {
11
+ status: status,
12
+ error: message
13
+ }
14
+ end
15
+
16
+ def message
17
+ "#{title} - #{body}"
18
+ end
19
+
20
+ def status
21
+ @payload[:status]
22
+ end
23
+
24
+ def title
25
+ @payload[:title]
26
+ end
27
+
28
+ def body
29
+ @payload[:body]
30
+ end
31
+
32
+ def to_s
33
+ "#{status} #{message}"
34
+ end
35
+
36
+ private
37
+
38
+ def bad_request
39
+ {
40
+ title: "BAD REQUEST",
41
+ body: "The request cannot be fulfilled due to bad syntax",
42
+ status: 400
43
+ }
44
+ end
45
+
46
+ def unprocessable_entity
47
+ {
48
+ title: "UNPROCESSABLE ENTITY",
49
+ body: "The request was well-formed but was unable to be followed due to semantic errors.",
50
+ status: 422
51
+ }
52
+ end
53
+
54
+ def not_found
55
+ {
56
+ title: "RESOURCE NOT FOUND",
57
+ body: "The requested resource could not be found but may be available again in the future. Subsequent requests by the client are permissible.",
58
+ status: 404
59
+ }
60
+ end
61
+
62
+ def internal_server_error
63
+ {
64
+ title: "INTERNAL SERVER ERROR",
65
+ body: "The server encountered an unexpected condition which prevented it from fulfilling the request.",
66
+ status: 500
67
+ }
68
+ end
69
+
70
+ def method_not_allowed
71
+ {
72
+ title: "Method Not Allowed",
73
+ body: "The method specified in the Request-Line is not allowed for the resource identified by the Request-URI. The response MUST include an Allow header containing a list of valid methods for the requested resource.",
74
+ status: 405
75
+ }
76
+ end
77
+ end
78
+ end
79
+ end