mooncell 0.1.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 +7 -0
- data/.gitignore +16 -0
- data/.overcommit.yml +32 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/README.md +138 -0
- data/Rakefile +8 -0
- data/bin/mooncell +8 -0
- data/lib/mooncell.rb +102 -0
- data/lib/mooncell/application.rb +114 -0
- data/lib/mooncell/application_configuration.rb +66 -0
- data/lib/mooncell/cli.rb +16 -0
- data/lib/mooncell/cli/commands.rb +34 -0
- data/lib/mooncell/cli/commands/base.rb +66 -0
- data/lib/mooncell/cli/commands/console.rb +35 -0
- data/lib/mooncell/cli/commands/registry.rb +105 -0
- data/lib/mooncell/cli/commands/server.rb +25 -0
- data/lib/mooncell/cli/commands/version.rb +21 -0
- data/lib/mooncell/command.rb +78 -0
- data/lib/mooncell/configuration.rb +51 -0
- data/lib/mooncell/connection.rb +43 -0
- data/lib/mooncell/connection_pool.rb +39 -0
- data/lib/mooncell/entity.rb +12 -0
- data/lib/mooncell/env.rb +67 -0
- data/lib/mooncell/environment.rb +160 -0
- data/lib/mooncell/loader.rb +62 -0
- data/lib/mooncell/protocol.rb +28 -0
- data/lib/mooncell/protocol/registry.rb +33 -0
- data/lib/mooncell/protocol/websocket.rb +70 -0
- data/lib/mooncell/protocol/websocket/server.rb +78 -0
- data/lib/mooncell/respond.rb +87 -0
- data/lib/mooncell/router.rb +56 -0
- data/lib/mooncell/version.rb +5 -0
- data/mooncell.gemspec +40 -0
- metadata +260 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent'
|
4
|
+
|
5
|
+
module Mooncell
|
6
|
+
# @since 0.1.0
|
7
|
+
# @api private
|
8
|
+
class Configuration
|
9
|
+
# @since 0.1.0
|
10
|
+
# @api private
|
11
|
+
def initialize(&block)
|
12
|
+
@settings = Concurrent::Map.new
|
13
|
+
instance_eval(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set or get application protocol
|
17
|
+
#
|
18
|
+
# @param name [String|NilClass] the protocol name
|
19
|
+
#
|
20
|
+
# @since 0.1.0
|
21
|
+
# @api private
|
22
|
+
def protocol(name = nil)
|
23
|
+
# TODO: Support multiple protocol each server
|
24
|
+
return settings[:protocol] if name.nil?
|
25
|
+
|
26
|
+
settings[:protocol] = name.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
# @since 0.1.0
|
30
|
+
# @api private
|
31
|
+
def apps
|
32
|
+
settings.fetch_or_store(:apps, {})
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set App
|
36
|
+
#
|
37
|
+
# @param application [Mooncell::Application] the application
|
38
|
+
#
|
39
|
+
# @since 0.1.0
|
40
|
+
# @api private
|
41
|
+
def app(app)
|
42
|
+
apps[app.app_name] = app.new
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# @since 0.1.0
|
48
|
+
# @api private
|
49
|
+
attr_reader :settings
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mooncell
|
4
|
+
# The connection wrapper
|
5
|
+
#
|
6
|
+
# @since 0.1.0
|
7
|
+
class Connection
|
8
|
+
# @since 0.1.0
|
9
|
+
# @api private
|
10
|
+
attr_reader :app
|
11
|
+
|
12
|
+
# Create a connection
|
13
|
+
#
|
14
|
+
# @param io [#write] the writable I/O
|
15
|
+
#
|
16
|
+
# @since 0.1.0
|
17
|
+
def initialize(io, app)
|
18
|
+
@io = io
|
19
|
+
@app = app
|
20
|
+
|
21
|
+
prepare
|
22
|
+
end
|
23
|
+
|
24
|
+
# Write data
|
25
|
+
#
|
26
|
+
# @param data [] the data to write
|
27
|
+
#
|
28
|
+
# @since 0.1.0
|
29
|
+
def write(data)
|
30
|
+
io.write(data)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :io
|
36
|
+
|
37
|
+
# @since 0.1.0
|
38
|
+
# @api private
|
39
|
+
def prepare
|
40
|
+
app.pool.add(self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent'
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
module Mooncell
|
7
|
+
# The connection pool
|
8
|
+
#
|
9
|
+
# @since 0.1.0
|
10
|
+
# @api private
|
11
|
+
class ConnectionPool
|
12
|
+
extend Forwardable
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
delegate %w[add delete] => 'connections'
|
16
|
+
|
17
|
+
# @since 0.1.0
|
18
|
+
# @api private
|
19
|
+
attr_reader :connections
|
20
|
+
|
21
|
+
# Create a new connection pool
|
22
|
+
#
|
23
|
+
# @since 0.1.0
|
24
|
+
# @api private
|
25
|
+
def initialize
|
26
|
+
@connections = Concurrent::Set.new
|
27
|
+
end
|
28
|
+
|
29
|
+
# Iterate connections
|
30
|
+
#
|
31
|
+
# @param block [Proc] the iterate block
|
32
|
+
#
|
33
|
+
# @since 0.1.0
|
34
|
+
# @api private
|
35
|
+
def each(&block)
|
36
|
+
@connections.each(&block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/mooncell/env.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dotenv'
|
4
|
+
|
5
|
+
module Mooncell
|
6
|
+
# Environment container
|
7
|
+
#
|
8
|
+
# @since 0.1.0
|
9
|
+
# @api private
|
10
|
+
class Env
|
11
|
+
# Create a new instance
|
12
|
+
#
|
13
|
+
# @param env [#[],#[]=] a Hash like object. Default is ENV
|
14
|
+
#
|
15
|
+
# @return [Mooncell::Env]
|
16
|
+
#
|
17
|
+
# @since 0.1.0
|
18
|
+
# @api private
|
19
|
+
def initialize(env: ENV)
|
20
|
+
@env = env
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return a value if found
|
24
|
+
#
|
25
|
+
# @param key [String] The key
|
26
|
+
#
|
27
|
+
# @return [String,NilClass] the value
|
28
|
+
#
|
29
|
+
# @since 0.1.0
|
30
|
+
# @api private
|
31
|
+
def [](key)
|
32
|
+
@env[key]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set a value
|
36
|
+
#
|
37
|
+
# @param key [String] the key
|
38
|
+
# @param value [String] the value
|
39
|
+
#
|
40
|
+
# @since 0.1.0
|
41
|
+
# @api private
|
42
|
+
def []=(key, value)
|
43
|
+
@env[key] = value
|
44
|
+
end
|
45
|
+
|
46
|
+
# Load environment from dotenv
|
47
|
+
#
|
48
|
+
# @param path [String, Pathname] the path to the dotenv file
|
49
|
+
#
|
50
|
+
# @return void
|
51
|
+
#
|
52
|
+
# @since 0.1.0
|
53
|
+
# @api private
|
54
|
+
def load!(path)
|
55
|
+
return unless defined?(Dotenv::Parser)
|
56
|
+
|
57
|
+
contents = ::File.open(path, 'rb:bom|utf-8', &:read)
|
58
|
+
parsed = Dotenv::Parser.call(contents)
|
59
|
+
|
60
|
+
parsed.each do |k, v|
|
61
|
+
next if @env.key?(k)
|
62
|
+
|
63
|
+
@env[k] = v
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mooncell/env'
|
4
|
+
|
5
|
+
module Mooncell
|
6
|
+
# Mooncell environment
|
7
|
+
#
|
8
|
+
# @since 0.1.0
|
9
|
+
# @api private
|
10
|
+
class Environment
|
11
|
+
# Global lock
|
12
|
+
#
|
13
|
+
# @since 0.1.0
|
14
|
+
# @api private
|
15
|
+
LOCK = Mutex.new
|
16
|
+
|
17
|
+
# Standard Mooncell ENV Key
|
18
|
+
#
|
19
|
+
# @since 0.1.0
|
20
|
+
# @api private
|
21
|
+
MOONCELL_ENV = 'MOONCELL_ENV'
|
22
|
+
|
23
|
+
# Default Mooncell environment
|
24
|
+
#
|
25
|
+
# @since 0.1.0
|
26
|
+
# @api private
|
27
|
+
DEFAULT_ENV = 'development'
|
28
|
+
|
29
|
+
# Mooncell test environment
|
30
|
+
#
|
31
|
+
# @since 0.1.0
|
32
|
+
# @api private
|
33
|
+
TEST_ENV = 'test'
|
34
|
+
|
35
|
+
# Mooncell production environment
|
36
|
+
#
|
37
|
+
# @since 0.1.0
|
38
|
+
# @api private
|
39
|
+
PRODUCTION_ENV = 'production'
|
40
|
+
|
41
|
+
# @since 0.1.0
|
42
|
+
# @api private
|
43
|
+
APPS_PATH = 'apps'
|
44
|
+
|
45
|
+
# @since 0.1.0
|
46
|
+
# @api private
|
47
|
+
DOTENV_LOCAL_FILE = '.env.local'
|
48
|
+
|
49
|
+
# Default `.env` files will be loaded
|
50
|
+
#
|
51
|
+
# @since 0.1.0
|
52
|
+
# @api private
|
53
|
+
DOTENV_FILES = [
|
54
|
+
'.env.%<environment>s.local',
|
55
|
+
DOTENV_LOCAL_FILE,
|
56
|
+
'.env.%<environment>s',
|
57
|
+
'.env'
|
58
|
+
].freeze
|
59
|
+
|
60
|
+
# @since 0.1.0
|
61
|
+
# @api private
|
62
|
+
CODE_RELOADING = {
|
63
|
+
'development' => true
|
64
|
+
}.freeze
|
65
|
+
|
66
|
+
# Initialize a Mooncell Environment
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
def initialize(options = {})
|
70
|
+
opts = options.to_h.dup
|
71
|
+
@env = Mooncell::Env.new(env: opts.delete(:env) || ENV)
|
72
|
+
LOCK.synchronize { load_env_vars! }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Application's root
|
76
|
+
#
|
77
|
+
# @return [Pathname] application's root
|
78
|
+
#
|
79
|
+
# @since 0.1.0
|
80
|
+
# @api private
|
81
|
+
def root
|
82
|
+
@root ||= Bundler.root
|
83
|
+
rescue Bundler::GemfileNotFound
|
84
|
+
@root = Pathname.new(Dir.pwd)
|
85
|
+
end
|
86
|
+
|
87
|
+
# The current environment
|
88
|
+
#
|
89
|
+
# @return [String] the current environment
|
90
|
+
#
|
91
|
+
# @since 0.1.0
|
92
|
+
# @api private
|
93
|
+
#
|
94
|
+
# @see Mooncell::Environment::DEFAULT_ENF
|
95
|
+
def environment
|
96
|
+
@environment ||= env[MOONCELL_ENV] || DEFAULT_ENV
|
97
|
+
end
|
98
|
+
|
99
|
+
# @since 0.1.0
|
100
|
+
# @api private
|
101
|
+
def apps_path
|
102
|
+
root.join(APPS_PATH)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Project environment configuration path
|
106
|
+
#
|
107
|
+
# @return [Pathname] path to application
|
108
|
+
#
|
109
|
+
# @since 0.1.0
|
110
|
+
# @api private
|
111
|
+
def config
|
112
|
+
root.join('config', 'environment.rb')
|
113
|
+
end
|
114
|
+
|
115
|
+
# @api private
|
116
|
+
alias project_config config
|
117
|
+
|
118
|
+
# Load project environment configuration
|
119
|
+
#
|
120
|
+
# @since 0.1.0
|
121
|
+
# @api private
|
122
|
+
def require_project_environment
|
123
|
+
require project_config.to_s
|
124
|
+
end
|
125
|
+
|
126
|
+
# Determine if activate code reloading for current environment
|
127
|
+
#
|
128
|
+
# @return [TrueClass,FalseClass] the result of check
|
129
|
+
#
|
130
|
+
# @since 0.1.0
|
131
|
+
# @api private
|
132
|
+
def code_reloading?
|
133
|
+
!!CODE_RELOADING[environment] # rubocop:disable Style/DoubleNegation
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
attr_reader :env
|
139
|
+
|
140
|
+
# @since 0.1.0
|
141
|
+
# @api private
|
142
|
+
def load_env_vars!
|
143
|
+
DOTENV_FILES.each do |filename_format|
|
144
|
+
file = format(filename_format, environment: environment)
|
145
|
+
next unless dotenv_applicable?(file)
|
146
|
+
|
147
|
+
path = root.join(file)
|
148
|
+
env.load!(path) if path.exist?
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# @since 0.1.0
|
153
|
+
# @api private
|
154
|
+
def dotenv_applicable?(file)
|
155
|
+
return false if file == DOTENV_LOCAL_FILE && environment == TEST_ENV
|
156
|
+
|
157
|
+
true
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'zeitwerk'
|
4
|
+
require 'singleton'
|
5
|
+
require 'forwardable'
|
6
|
+
|
7
|
+
module Mooncell
|
8
|
+
# Code Loader
|
9
|
+
#
|
10
|
+
# @since 0.1.0
|
11
|
+
# @api private
|
12
|
+
class Loader
|
13
|
+
class << self
|
14
|
+
extend Forwardable
|
15
|
+
|
16
|
+
delegate [
|
17
|
+
'setup',
|
18
|
+
'reload!'
|
19
|
+
] => 'instance'
|
20
|
+
end
|
21
|
+
|
22
|
+
include Singleton
|
23
|
+
|
24
|
+
# @since 0.1.0
|
25
|
+
# @api private
|
26
|
+
def initialize
|
27
|
+
@loader = Zeitwerk::Loader.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# Setup Loader
|
31
|
+
#
|
32
|
+
# @since 0.1.0
|
33
|
+
# @api private
|
34
|
+
def setup
|
35
|
+
prepare
|
36
|
+
# TODO: Code Reloading
|
37
|
+
loader.enable_reloading if Mooncell.code_reloading?
|
38
|
+
loader.setup
|
39
|
+
# TODO: Eager Load
|
40
|
+
end
|
41
|
+
|
42
|
+
# Reload Code
|
43
|
+
#
|
44
|
+
# @since 0.1.0
|
45
|
+
# @api private
|
46
|
+
def reload!
|
47
|
+
loader.reload
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @since 0.1.0
|
53
|
+
# @api private
|
54
|
+
attr_reader :loader
|
55
|
+
|
56
|
+
# @since 0.1.0
|
57
|
+
# @api private
|
58
|
+
def prepare
|
59
|
+
loader.push_dir(Mooncell.environment.apps_path)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent'
|
4
|
+
|
5
|
+
module Mooncell
|
6
|
+
# Mooncell Network Protocol
|
7
|
+
#
|
8
|
+
# @since 0.1.0
|
9
|
+
# @api private
|
10
|
+
module Protocol
|
11
|
+
require 'mooncell/protocol/registry'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# @since 0.1.0
|
15
|
+
# @api private
|
16
|
+
def included(base)
|
17
|
+
super
|
18
|
+
Registry.register(base)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @since 0.1.0
|
22
|
+
# @api private
|
23
|
+
def get(name)
|
24
|
+
Registry.lookup(name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|