mooncell 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sequel'
4
+
5
+ module Mooncell
6
+ Entity = Class.new(Sequel::Model)
7
+ # The Data Model
8
+ #
9
+ # @since 0.1.0
10
+ class Entity
11
+ end
12
+ end
@@ -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