whiskey 1.0.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.
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