appril 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|