appril 0.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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/app/.gitignore +6 -0
- data/app/.pryrc +1 -0
- data/app/Gemfile +5 -0
- data/app/Rakefile +1 -0
- data/app/base/api/.ignore +0 -0
- data/app/base/base_controller.rb +3 -0
- data/app/base/boot.rb +2 -0
- data/app/base/core.coffee +91 -0
- data/app/base/helpers/application_helpers.rb +1 -0
- data/app/base/load.rb +0 -0
- data/app/base/load_controllers.rb +2 -0
- data/app/base/rtcp_controller.rb +22 -0
- data/app/config/config.rb +9 -0
- data/app/config/config.yml +18 -0
- data/app/config/env/development.yml +0 -0
- data/app/config/env/production.yml +0 -0
- data/app/config/env/stage.yml +0 -0
- data/app/config/env/test.yml +0 -0
- data/app/config.ru +3 -0
- data/app/core/Gemfile +4 -0
- data/app/core/boot.rb +16 -0
- data/app/core/generate_configs.rb +7 -0
- data/app/core/load.rb +7 -0
- data/app/core/load_controllers.rb +15 -0
- data/app/generators/api/.ignore +0 -0
- data/app/package.json +5 -0
- data/app/public/.ignore +0 -0
- data/app/webpack.config.js +48 -0
- data/appril.gemspec +22 -0
- data/bin/appril +5 -0
- data/docker/Dockerfile +5 -0
- data/docker/run +68 -0
- data/docker/skel/build.sh +1 -0
- data/docker/skel/cleanup.sh +7 -0
- data/docker/skel/config.yml +24 -0
- data/docker/skel/prepare_build.sh +5 -0
- data/docker/skel/start.sh +1 -0
- data/docker/start +7 -0
- data/lib/appril/base_controller.rb +5 -0
- data/lib/appril/cli/app/install.rb +42 -0
- data/lib/appril/cli/app/update.rb +26 -0
- data/lib/appril/cli/app.rb +10 -0
- data/lib/appril/cli/assertions.rb +60 -0
- data/lib/appril/cli/docker/build.rb +150 -0
- data/lib/appril/cli/docker/install.rb +21 -0
- data/lib/appril/cli/docker/update.rb +24 -0
- data/lib/appril/cli/docker.rb +16 -0
- data/lib/appril/cli/generator.rb +15 -0
- data/lib/appril/cli/helpers.rb +47 -0
- data/lib/appril/cli.rb +197 -0
- data/lib/appril/rtcp_controller.rb +103 -0
- data/lib/appril/version.rb +3 -0
- data/lib/appril.rb +124 -0
- metadata +128 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
module Appril
|
2
|
+
class CLI
|
3
|
+
module Docker
|
4
|
+
class Build
|
5
|
+
|
6
|
+
def initialize dir, update_runner_only: false, push_opted: false
|
7
|
+
Dir.chdir dir do
|
8
|
+
|
9
|
+
config = load_config
|
10
|
+
validate_config(config)
|
11
|
+
|
12
|
+
build_dir = Pathname.new(File.expand_path('__tmpbuildir__'))
|
13
|
+
|
14
|
+
prepare_build_dir(build_dir)
|
15
|
+
|
16
|
+
install_files(build_dir)
|
17
|
+
|
18
|
+
app_dir = Pathname.new(File.expand_path(config['APP_DIR']))
|
19
|
+
|
20
|
+
if update_runner_only
|
21
|
+
puts "Skipping image building"
|
22
|
+
else
|
23
|
+
prepare_build(build_dir, app_dir)
|
24
|
+
|
25
|
+
image_built = build_image(build_dir, config['IMAGE_NAME'], config['BUILD_OPTS'])
|
26
|
+
|
27
|
+
if image_built && push_opted
|
28
|
+
push_image(config['IMAGE_NAME'])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
puts "Installing run script"
|
33
|
+
install_run_script(app_dir, *config.values_at('IMAGE_NAME', 'CONTAINER_NAME', 'RUN_SCRIPT', 'RUN_OPTS'))
|
34
|
+
|
35
|
+
FileUtils.rm_rf(build_dir)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_config
|
40
|
+
YAML.load(File.read(CONFIG_FILE))
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_config config
|
44
|
+
{
|
45
|
+
'IMAGE_NAME' => :validate_config__image_name,
|
46
|
+
'CONTAINER_NAME' => :validate_config__container_name,
|
47
|
+
'APP_DIR' => :validate_config__app_dir,
|
48
|
+
'RUN_SCRIPT' => :validate_config__run_script,
|
49
|
+
}.each_pair do |key,validator|
|
50
|
+
next if send(validator, config[key])
|
51
|
+
puts "", "\t::: Please set #{key} in config.yml :::", ""
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_config__image_name value
|
57
|
+
!value.nil? && !value.empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_config__container_name value
|
61
|
+
!value.nil? && !value.empty? && value.values.any?
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_config__app_dir value
|
65
|
+
!value.nil? && !value.empty?
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_config__run_script value
|
69
|
+
!value.nil? && !value.empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
def prepare_build_dir dir
|
73
|
+
FileUtils.rm_rf(dir)
|
74
|
+
FileUtils.mkdir_p(dir / 'build')
|
75
|
+
end
|
76
|
+
|
77
|
+
def install_files dir
|
78
|
+
install_dockerfile(dir)
|
79
|
+
install_start_file(dir)
|
80
|
+
install_build_files(dir)
|
81
|
+
install_cleanup_file(dir)
|
82
|
+
end
|
83
|
+
|
84
|
+
def install_dockerfile dir
|
85
|
+
FileUtils.cp(BASE_DIR / 'Dockerfile', dir)
|
86
|
+
end
|
87
|
+
|
88
|
+
def install_start_file dir
|
89
|
+
File.open dir / 'start', 'w' do |f|
|
90
|
+
f << File.read(BASE_DIR / 'start').sub('{start}', File.read(START_FILE))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def install_build_files dir
|
95
|
+
FileUtils.cp(PREPARE_BUILD_FILE, dir / 'build')
|
96
|
+
FileUtils.cp(BUILD_FILE, dir / 'build/build')
|
97
|
+
end
|
98
|
+
|
99
|
+
def install_cleanup_file dir
|
100
|
+
FileUtils.cp(CLEANUP_FILE, dir / 'build/cleanup')
|
101
|
+
end
|
102
|
+
|
103
|
+
def prepare_build dir, app_dir
|
104
|
+
Dir.chdir dir / 'build' do
|
105
|
+
CLI.run "APP_DIR=#{app_dir} #{PREPARE_BUILD_FILE}"
|
106
|
+
exit 1 unless $? && $?.success?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def build_image dir, image_name, build_opts
|
111
|
+
CLI.run "docker build -t #{image_name} #{build_opts} '#{dir}'"
|
112
|
+
$? && $?.success?
|
113
|
+
end
|
114
|
+
|
115
|
+
def push_image image_name
|
116
|
+
CLI.run "docker push #{image_name}"
|
117
|
+
$? && $?.success?
|
118
|
+
end
|
119
|
+
|
120
|
+
def install_run_script app_dir, image_name, container_name, run_script, run_opts
|
121
|
+
FileUtils.mkdir_p(app_dir / File.dirname(run_script))
|
122
|
+
|
123
|
+
script_path = app_dir / run_script
|
124
|
+
|
125
|
+
File.open script_path, 'w' do |f|
|
126
|
+
f << File.read(BASE_DIR / 'run').
|
127
|
+
gsub('{image}', image_name).
|
128
|
+
gsub('{script_path_traversal}', script_path_traversal(run_script)).
|
129
|
+
gsub('{run_opts}', run_opts).
|
130
|
+
gsub('{environments}', container_name.keys.join(' ')).
|
131
|
+
gsub('{container_definitions}', container_definitions(container_name))
|
132
|
+
end
|
133
|
+
|
134
|
+
FileUtils.chmod('+x', script_path)
|
135
|
+
end
|
136
|
+
|
137
|
+
def container_definitions container_name
|
138
|
+
container_name.map do |kv|
|
139
|
+
'[ "$APP_ENV" = "%s" ] && CONTAINER_NAME="%s"' % kv
|
140
|
+
end.join("\n")
|
141
|
+
end
|
142
|
+
|
143
|
+
def script_path_traversal run_script
|
144
|
+
run_script.gsub(/\A\/+|\/+\Z/, '').scan(/\/+/).map {'..'}*'/'
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Appril
|
2
|
+
class CLI
|
3
|
+
module Docker
|
4
|
+
class Install
|
5
|
+
include Helpers
|
6
|
+
|
7
|
+
def initialize dir, working_dir_opted: false
|
8
|
+
install(dir, working_dir_opted)
|
9
|
+
make_executable(dir / PREPARE_BUILD_FILE)
|
10
|
+
puts "Done. All files installed into #{dir}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def install dir, working_dir_opted
|
14
|
+
src = working_dir_opted ? BASE_DIR.to_path + '/skel/.' : BASE_DIR / 'skel'
|
15
|
+
FileUtils.cp_r(src, dir)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Appril
|
2
|
+
class CLI
|
3
|
+
module Docker
|
4
|
+
class Update
|
5
|
+
include Helpers
|
6
|
+
|
7
|
+
def initialize dir
|
8
|
+
Dir.chdir BASE_DIR / 'skel' do
|
9
|
+
|
10
|
+
Dir['**/*'].select {|e| File.file?(e)}.each do |file|
|
11
|
+
next if File.file?(dir / file)
|
12
|
+
create_dirname_for(dir / file)
|
13
|
+
puts "Installing #{File.basename(dir)}/#{file}"
|
14
|
+
FileUtils.cp(file, dir / file)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
puts "Done"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Appril
|
2
|
+
class CLI
|
3
|
+
module Docker
|
4
|
+
BASE_DIR = (Appril::BASE_DIR / 'docker').freeze
|
5
|
+
CONFIG_FILE = './config.yml'.freeze
|
6
|
+
START_FILE = './start.sh'.freeze
|
7
|
+
PREPARE_BUILD_FILE = './prepare_build.sh'.freeze
|
8
|
+
BUILD_FILE = './build.sh'.freeze
|
9
|
+
CLEANUP_FILE = './cleanup.sh'.freeze
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'appril/cli/docker/install'
|
15
|
+
require 'appril/cli/docker/build'
|
16
|
+
require 'appril/cli/docker/update'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Appril
|
2
|
+
class CLI
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def display_error error
|
6
|
+
puts "", "\t::: #{error} :::", ""
|
7
|
+
end
|
8
|
+
|
9
|
+
def fatal_error! error, exit_code = 1
|
10
|
+
display_error(error)
|
11
|
+
exit exit_code
|
12
|
+
end
|
13
|
+
|
14
|
+
def expanded_path *path
|
15
|
+
Pathname.new(File.expand_path(File.join(*path.map(&:to_s))))
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_dirname_for dir
|
19
|
+
FileUtils.mkdir_p(File.dirname(dir))
|
20
|
+
end
|
21
|
+
|
22
|
+
def make_executable *entries
|
23
|
+
entries.flatten.each {|e| FileUtils.chmod('+x', e)}
|
24
|
+
end
|
25
|
+
|
26
|
+
def working_dir_opted? opted_dir
|
27
|
+
opted_dir == '.'
|
28
|
+
end
|
29
|
+
|
30
|
+
def extract_namespace args
|
31
|
+
return unless index = args.index('-n')
|
32
|
+
return unless namespace = args[index + 1]
|
33
|
+
if namespace =~ /::/
|
34
|
+
fatal_error! "Nested namespaces not supported"
|
35
|
+
end
|
36
|
+
if namespace =~ /\W/
|
37
|
+
fatal_error! "Namespace may contain only alphanumerics"
|
38
|
+
end
|
39
|
+
unless namespace =~ /\A[A-Z]/
|
40
|
+
fatal_error! "Namespace should start with a capital letter"
|
41
|
+
end
|
42
|
+
namespace
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/appril/cli.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
require 'yaml'
|
4
|
+
require 'pty'
|
5
|
+
require 'appril/version'
|
6
|
+
require 'appril/cli/helpers'
|
7
|
+
require 'appril/cli/assertions'
|
8
|
+
|
9
|
+
module Appril
|
10
|
+
BASE_DIR = Pathname.new(File.expand_path('../../..', __FILE__)).freeze
|
11
|
+
|
12
|
+
class CLI
|
13
|
+
include Helpers
|
14
|
+
include Assertions
|
15
|
+
|
16
|
+
def initialize args
|
17
|
+
case command = args[0]
|
18
|
+
when 'a', 'app'
|
19
|
+
app(args)
|
20
|
+
when 'g', 'gen', 'generate'
|
21
|
+
generator(args)
|
22
|
+
when 'd', 'docker'
|
23
|
+
docker(args)
|
24
|
+
when 'v', '-v', '--version'
|
25
|
+
puts Appril::VERSION
|
26
|
+
when nil, '-h', '--help'
|
27
|
+
usage
|
28
|
+
else
|
29
|
+
display_error "Unknown command #{command}"
|
30
|
+
usage
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def usage
|
36
|
+
puts "
|
37
|
+
=== Install a new app ===
|
38
|
+
$ appril [app || a] [install || i] [dir || .]
|
39
|
+
|
40
|
+
=== Update existing app ===
|
41
|
+
$ appril [app || a] [update || u] [dir || .]
|
42
|
+
|
43
|
+
=== Generate a new API ===
|
44
|
+
$ appril [generate || g] [api || a] [api name] [dir || .]
|
45
|
+
|
46
|
+
=== Install Docker recipes ===
|
47
|
+
$ appril [docker || d] [install || i] [dir || .]
|
48
|
+
|
49
|
+
=== Update Docker recipes ===
|
50
|
+
$ appril [docker || d] [update || u] [dir || .]
|
51
|
+
|
52
|
+
=== Build Docker image and install run script ===
|
53
|
+
$ appril [docker || d] [build || b] [dir || .] [-u] [-p]
|
54
|
+
If -u option provided it will only update the run script without building the image.
|
55
|
+
If -p option provided it will try to push the image to Docker registry after successful build.
|
56
|
+
|
57
|
+
=== Usage ===
|
58
|
+
$ appril [-h || --help]
|
59
|
+
".split("\n").map {|l| "\t" + l.strip}.join("\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
begin # App
|
64
|
+
def app args
|
65
|
+
opted_dir = args[2]
|
66
|
+
assert_directory_provided(opted_dir)
|
67
|
+
dir = expanded_path(opted_dir)
|
68
|
+
|
69
|
+
case instruction = args[1]
|
70
|
+
when 'i', 'install'
|
71
|
+
|
72
|
+
app_install(dir, {
|
73
|
+
working_dir_opted: working_dir_opted?(opted_dir),
|
74
|
+
namespace: extract_namespace(args)
|
75
|
+
})
|
76
|
+
|
77
|
+
when 'u', 'update'
|
78
|
+
|
79
|
+
app_update(dir)
|
80
|
+
|
81
|
+
else
|
82
|
+
unknown_instruction_error!(instruction, 'install (or i)', 'update (or u)')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def app_install dir, opts
|
87
|
+
create_dirname_for(dir)
|
88
|
+
assert_installable_dir(dir, opts[:working_dir_opted])
|
89
|
+
App::Install.new(dir, opts)
|
90
|
+
end
|
91
|
+
|
92
|
+
def app_update dir
|
93
|
+
assert_is_app_dir(dir)
|
94
|
+
App::Update.new(dir)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
begin # Generator
|
100
|
+
def generator args
|
101
|
+
|
102
|
+
case instruction = args[1]
|
103
|
+
when 'api', 'a'
|
104
|
+
api_name = args[2]
|
105
|
+
assert_valid_api_name_given(api_name)
|
106
|
+
|
107
|
+
app_dir = args[3]
|
108
|
+
assert_directory_provided(app_dir)
|
109
|
+
app_dir = expanded_path(app_dir)
|
110
|
+
assert_is_app_dir(app_dir)
|
111
|
+
|
112
|
+
gen_dir = app_dir / 'generators/api'
|
113
|
+
assert_directory_exists(gen_dir)
|
114
|
+
|
115
|
+
api_dir = app_dir / "base/api/#{api_name}"
|
116
|
+
assert_directory_does_not_exists(api_dir)
|
117
|
+
|
118
|
+
Generator::API.new(gen_dir, api_dir, api_name)
|
119
|
+
else
|
120
|
+
unknown_instruction_error!(instruction, 'api (or a)')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
begin # Docker
|
127
|
+
def docker args
|
128
|
+
opted_dir = args[2]
|
129
|
+
assert_directory_provided(opted_dir)
|
130
|
+
dir = expanded_path(opted_dir)
|
131
|
+
|
132
|
+
case instruction = args[1]
|
133
|
+
when 'i', 'install'
|
134
|
+
|
135
|
+
docker_install(dir, working_dir_opted: working_dir_opted?(opted_dir))
|
136
|
+
|
137
|
+
when 'b', 'build'
|
138
|
+
|
139
|
+
docker_build(dir, {
|
140
|
+
update_runner_only: args.find {|a| a == '-u'},
|
141
|
+
push_opted: args.find {|a| a == '-p'}
|
142
|
+
})
|
143
|
+
|
144
|
+
when 'u', 'update'
|
145
|
+
|
146
|
+
docker_update(dir)
|
147
|
+
|
148
|
+
else
|
149
|
+
unknown_instruction_error!(instruction, 'install (or i)', 'build (or b)', 'update (or u)')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def docker_install dir, opts
|
154
|
+
create_dirname_for(dir)
|
155
|
+
assert_installable_dir(dir, opts[:working_dir_opted])
|
156
|
+
Docker::Install.new(dir, opts)
|
157
|
+
end
|
158
|
+
|
159
|
+
def docker_build dir, opts
|
160
|
+
assert_directory_exists(dir)
|
161
|
+
assert_config_file_exists(dir)
|
162
|
+
Docker::Build.new(dir, opts)
|
163
|
+
end
|
164
|
+
|
165
|
+
def docker_update dir
|
166
|
+
assert_is_docker_dir(dir)
|
167
|
+
Docker::Update.new(dir)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
def unknown_instruction_error! instruction, *available_instructions
|
173
|
+
fatal_error! "Unknown instruction #{instruction}. Use one of #{available_instructions*', '}"
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
def self.run cmd
|
178
|
+
puts "", "$ #{cmd}"
|
179
|
+
PTY.spawn cmd do |r, w, pid|
|
180
|
+
begin
|
181
|
+
r.sync
|
182
|
+
r.each_char do |char|
|
183
|
+
print(char)
|
184
|
+
end
|
185
|
+
rescue Errno::EIO => e
|
186
|
+
# simply ignoring this
|
187
|
+
ensure
|
188
|
+
Process.wait(pid)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
require 'appril/cli/app'
|
196
|
+
require 'appril/cli/generator'
|
197
|
+
require 'appril/cli/docker'
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Appril
|
2
|
+
class RTCPController < BaseController
|
3
|
+
attr_reader :socket
|
4
|
+
|
5
|
+
def get
|
6
|
+
return unless websocket?
|
7
|
+
|
8
|
+
@router = RocketIO::Router.new(*RocketIO.controllers)
|
9
|
+
|
10
|
+
@socket = Tubesock.hijack(env)
|
11
|
+
@socket.onopen(&method(:on_open))
|
12
|
+
@socket.onmessage(&method(:on_message))
|
13
|
+
@socket.onclose(&method(:on_close))
|
14
|
+
@socket.listen
|
15
|
+
|
16
|
+
halt websocket_response
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def on_open
|
22
|
+
connected
|
23
|
+
write(serial: 0, data: __initialization_data__.update(initialization_data))
|
24
|
+
end
|
25
|
+
|
26
|
+
def __initialization_data__
|
27
|
+
{
|
28
|
+
client_url: Cfg.client_url
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_message msg
|
33
|
+
|
34
|
+
msg = indifferent_params(JSON.parse(msg))
|
35
|
+
if controller = resolve_controller(msg[:controller])
|
36
|
+
status, _, body = call_controller(controller, msg[:method], msg[:arguments], msg[:serial])
|
37
|
+
if body.is_a?(Proc)
|
38
|
+
body.call
|
39
|
+
return
|
40
|
+
end
|
41
|
+
if status == 200
|
42
|
+
write(serial: msg[:serial], data: body) if msg[:reply]
|
43
|
+
return
|
44
|
+
end
|
45
|
+
else
|
46
|
+
body = '404: Not Found'
|
47
|
+
end
|
48
|
+
|
49
|
+
error = if body.is_a?(Array) && body.size == 1
|
50
|
+
body[0]
|
51
|
+
else
|
52
|
+
body
|
53
|
+
end
|
54
|
+
|
55
|
+
if msg && msg[:serial]
|
56
|
+
write(serial: msg[:serial], error: error)
|
57
|
+
else
|
58
|
+
write(error: error)
|
59
|
+
end
|
60
|
+
|
61
|
+
rescue Errno::EPIPE => e
|
62
|
+
close
|
63
|
+
rescue Exception => e
|
64
|
+
__error__(500, e)
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_close
|
68
|
+
@socket.close! if @socket
|
69
|
+
@socket, @user, @router = nil
|
70
|
+
disconnected
|
71
|
+
end
|
72
|
+
|
73
|
+
def resolve_controller url
|
74
|
+
@router.resolve_path(url)[0]
|
75
|
+
end
|
76
|
+
|
77
|
+
def call_controller controller, method, arguments, serial
|
78
|
+
controller.initialize_controller(method, arguments).call(env.merge(rtcp_serial: serial).update(rtcp_env))
|
79
|
+
end
|
80
|
+
|
81
|
+
def write data
|
82
|
+
@socket.send_data(data.to_json)
|
83
|
+
end
|
84
|
+
|
85
|
+
# called after socket connection established
|
86
|
+
def connected
|
87
|
+
end
|
88
|
+
|
89
|
+
# data sent to client after connection established
|
90
|
+
def initialization_data
|
91
|
+
{}
|
92
|
+
end
|
93
|
+
|
94
|
+
# merged into original env when calling a controller
|
95
|
+
def rtcp_env
|
96
|
+
{}
|
97
|
+
end
|
98
|
+
|
99
|
+
# called when socket connection closed
|
100
|
+
def disconnected
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/appril.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'rocketio'
|
2
|
+
|
3
|
+
module Appril
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def load_config dir, env = RocketIO.environment
|
7
|
+
|
8
|
+
config = load_config_file("#{dir}/config.yml")
|
9
|
+
config.update(load_config_file("#{dir}/env/#{env}.yml"))
|
10
|
+
config[:environment] = env.to_s.freeze
|
11
|
+
|
12
|
+
Dir["#{dir}/**/*.yml"].each do |file|
|
13
|
+
next if File.dirname(file) == './env'
|
14
|
+
|
15
|
+
key = File.basename(file, '.yml')
|
16
|
+
next if key == 'config' || key == 'appril'
|
17
|
+
|
18
|
+
key_config = load_config_file(file)
|
19
|
+
key_config_keys = key_config.keys.map(&:to_s)
|
20
|
+
|
21
|
+
config[key] = if key_config_keys.include?(env.to_s)
|
22
|
+
# current environment found, use it
|
23
|
+
key_config[env]
|
24
|
+
else
|
25
|
+
if RocketIO::ENVIRONMENTS.keys.find {|k| key_config_keys.include?(k)}
|
26
|
+
# there are some environment(s), but no current one so set current environment to nil
|
27
|
+
nil
|
28
|
+
else
|
29
|
+
# there are no environments, so this config is available on any environment
|
30
|
+
key_config
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def config.method_missing key
|
37
|
+
self[key]
|
38
|
+
end
|
39
|
+
|
40
|
+
config
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def load_config_file file
|
45
|
+
RocketIO.indifferent_params(YAML.load(File.read(file)) || {})
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def controllers_map dir
|
50
|
+
path_to_api = File.expand_path('base/api', dir)
|
51
|
+
RocketIO.controllers.each_with_object([]) do |controller,o|
|
52
|
+
next unless controller.dirname[path_to_api]
|
53
|
+
|
54
|
+
o << {
|
55
|
+
path: controller.dirname.sub(path_to_api, '').gsub(/\A\/|\/\Z/, ''),
|
56
|
+
url: controller.url,
|
57
|
+
url_pattern: url_pattern(controller),
|
58
|
+
name: controller.name.gsub('::', '__'),
|
59
|
+
api: controller.api
|
60
|
+
}
|
61
|
+
end.sort do |a,b|
|
62
|
+
b[:url].split('/').size <=> a[:url].split('/').size
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def webpack_entries dir, controllers
|
68
|
+
entries = controllers.each_with_object({}) do |controller,o|
|
69
|
+
|
70
|
+
next unless entry = %w[
|
71
|
+
./base/api/%s/client.js
|
72
|
+
./base/api/%s/client.coffee
|
73
|
+
].map {|p| p % controller[:path]}.find {|f| File.file?(File.expand_path(f, dir))}
|
74
|
+
|
75
|
+
o[controller[:path]] = File.join('./base/api', controller[:path], File.basename(entry))
|
76
|
+
end
|
77
|
+
|
78
|
+
if core = %w[
|
79
|
+
./base/core.js
|
80
|
+
./base/core.coffee
|
81
|
+
].find {|f| File.file?(File.expand_path(f, dir))}
|
82
|
+
entries[:core] = core
|
83
|
+
end
|
84
|
+
|
85
|
+
entries
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def generate_configs dir
|
90
|
+
config = load_config("#{dir}/config", :development)
|
91
|
+
|
92
|
+
controllers = controllers_map(dir)
|
93
|
+
webpack_entries = webpack_entries(dir, controllers)
|
94
|
+
|
95
|
+
File.open File.expand_path('config.json', dir), 'w' do |f|
|
96
|
+
f << JSON.pretty_generate({
|
97
|
+
controllers: controllers,
|
98
|
+
webpack: {
|
99
|
+
path: config[:client_path],
|
100
|
+
url: config[:client_url],
|
101
|
+
entries: webpack_entries
|
102
|
+
}
|
103
|
+
})
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def url_pattern controller
|
109
|
+
controller.url *controller.instance_method(:get).parameters.each_with_object([]) {|param,o|
|
110
|
+
pattern = if param[0] == :rest
|
111
|
+
"*"
|
112
|
+
elsif param[0] == :req
|
113
|
+
":#{param[1]}"
|
114
|
+
elsif param[0] == :opt
|
115
|
+
":#{param[1]}?"
|
116
|
+
end
|
117
|
+
o << pattern if pattern
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
require 'appril/base_controller'
|
124
|
+
require 'appril/rtcp_controller'
|