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.
@@ -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