silicon 0.2.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/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +372 -0
- data/Rakefile +6 -0
- data/bin/console +7 -0
- data/bin/setup +8 -0
- data/exe/silicon +5 -0
- data/lib/silicon.rb +6 -0
- data/lib/silicon/app.rb +90 -0
- data/lib/silicon/base/handle_errors.rb +16 -0
- data/lib/silicon/chain.rb +69 -0
- data/lib/silicon/chain_factory.rb +39 -0
- data/lib/silicon/config.rb +15 -0
- data/lib/silicon/errors/silicon_error.rb +4 -0
- data/lib/silicon/errors/syntax_error.rb +6 -0
- data/lib/silicon/errors/view_engine_error.rb +6 -0
- data/lib/silicon/extensions/hash.rb +18 -0
- data/lib/silicon/generators/cli.rb +23 -0
- data/lib/silicon/generators/templates/actions/common/handle_errors.rb +2 -0
- data/lib/silicon/generators/templates/actions/welcome.tt +9 -0
- data/lib/silicon/generators/templates/app.rb +4 -0
- data/lib/silicon/generators/templates/app.routes +4 -0
- data/lib/silicon/generators/templates/config.ru +8 -0
- data/lib/silicon/generators/templates/silicon.yml +7 -0
- data/lib/silicon/generators/templates/views/show_welcome.json.jbuilder +1 -0
- data/lib/silicon/loaders/dependency_loader.rb +14 -0
- data/lib/silicon/loaders/template_loader.rb +20 -0
- data/lib/silicon/loaders/type_loader.rb +25 -0
- data/lib/silicon/request.rb +65 -0
- data/lib/silicon/routing/builder.rb +96 -0
- data/lib/silicon/routing/file_reader.rb +27 -0
- data/lib/silicon/routing/match.rb +12 -0
- data/lib/silicon/routing/matcher.rb +40 -0
- data/lib/silicon/routing/parser.rb +25 -0
- data/lib/silicon/routing/route.rb +19 -0
- data/lib/silicon/routing/routing.rb +11 -0
- data/lib/silicon/routing/syntax.rb +28 -0
- data/lib/silicon/routing/syntax/action.rb +30 -0
- data/lib/silicon/routing/syntax/actions.rb +15 -0
- data/lib/silicon/routing/syntax/after_section.rb +19 -0
- data/lib/silicon/routing/syntax/before_section.rb +18 -0
- data/lib/silicon/routing/syntax/catch_section.rb +15 -0
- data/lib/silicon/routing/syntax/command.rb +27 -0
- data/lib/silicon/routing/syntax/commands.rb +7 -0
- data/lib/silicon/routing/syntax/node.rb +47 -0
- data/lib/silicon/routing/syntax/nodes.rb +13 -0
- data/lib/silicon/routing/syntax/primitives/arrow.rb +4 -0
- data/lib/silicon/routing/syntax/primitives/back_arrow.rb +4 -0
- data/lib/silicon/routing/syntax/primitives/eol.rb +4 -0
- data/lib/silicon/routing/syntax/primitives/http_status.rb +13 -0
- data/lib/silicon/routing/syntax/primitives/http_verb.rb +4 -0
- data/lib/silicon/routing/syntax/primitives/indent.rb +4 -0
- data/lib/silicon/routing/syntax/primitives/parameter.rb +4 -0
- data/lib/silicon/routing/syntax/primitives/path.rb +5 -0
- data/lib/silicon/routing/syntax/respond.rb +25 -0
- data/lib/silicon/routing/syntax/route.rb +25 -0
- data/lib/silicon/routing/syntax/sections.rb +19 -0
- data/lib/silicon/routing/syntax/tree_section.rb +11 -0
- data/lib/silicon/routing/syntax/view.rb +7 -0
- data/lib/silicon/routing/syntax_error_interpreter.rb +28 -0
- data/lib/silicon/routing/syntax_grammar.tt +95 -0
- data/lib/silicon/template_registry.rb +17 -0
- data/lib/silicon/version.rb +3 -0
- data/lib/silicon/view_builder.rb +18 -0
- data/lib/silicon/view_builder_registry.rb +21 -0
- data/lib/silicon/view_builders/json.rb +16 -0
- data/lib/silicon/view_factory.rb +21 -0
- data/silicon.gemspec +45 -0
- metadata +272 -0
data/bin/setup
ADDED
data/exe/silicon
ADDED
data/lib/silicon.rb
ADDED
data/lib/silicon/app.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'hypo'
|
2
|
+
require 'json'
|
3
|
+
require 'rack/parser'
|
4
|
+
require 'silicon/loaders/type_loader'
|
5
|
+
require 'silicon/loaders/dependency_loader'
|
6
|
+
require 'silicon/loaders/template_loader'
|
7
|
+
require 'silicon/config'
|
8
|
+
require 'silicon/request'
|
9
|
+
require 'silicon/template_registry'
|
10
|
+
require 'silicon/view_builder_registry'
|
11
|
+
require 'silicon/view_factory'
|
12
|
+
require 'silicon/chain_factory'
|
13
|
+
require 'silicon/view_builders/json'
|
14
|
+
require 'silicon/routing/routing'
|
15
|
+
|
16
|
+
module Silicon
|
17
|
+
class App
|
18
|
+
def initialize(container = Hypo::Container.new)
|
19
|
+
@container = container
|
20
|
+
|
21
|
+
register_dependencies
|
22
|
+
load_basic_dependencies
|
23
|
+
parse_routes
|
24
|
+
add_view_builders
|
25
|
+
end
|
26
|
+
|
27
|
+
def call(env)
|
28
|
+
request = Request.new(@route_matcher, @container, @chain_factory, @view_factory)
|
29
|
+
request.handle(env)
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def add_view_builder(view_builder, format)
|
35
|
+
@view_builder_registry.add(view_builder, format)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def add_view_builders
|
41
|
+
add_view_builder(ViewBuilders::Json, 'json')
|
42
|
+
end
|
43
|
+
|
44
|
+
def register_dependencies
|
45
|
+
instances = {
|
46
|
+
config_file_path: 'silicon.yaml'
|
47
|
+
}
|
48
|
+
|
49
|
+
instances.keys.each do |name|
|
50
|
+
@container.register_instance(instances[name], name)
|
51
|
+
end
|
52
|
+
|
53
|
+
singletons = {
|
54
|
+
silicon_config: Config,
|
55
|
+
type_loader: TypeLoader,
|
56
|
+
dependency_loader: DependencyLoader,
|
57
|
+
template_loader: TemplateLoader,
|
58
|
+
template_registry: TemplateRegistry,
|
59
|
+
view_builder_registry: ViewBuilderRegistry,
|
60
|
+
view_factory: ViewFactory,
|
61
|
+
chain_factory: ChainFactory,
|
62
|
+
file_reader: Routing::FileReader,
|
63
|
+
syntax_parser: SyntaxParser,
|
64
|
+
syntax_error_interpreter: Routing::SyntaxErrorInterpreter,
|
65
|
+
routes_builder: Routing::Builder,
|
66
|
+
route_parser: Routing::Parser
|
67
|
+
}
|
68
|
+
|
69
|
+
singletons.keys.each do |name|
|
70
|
+
@container.register(singletons[name], name)
|
71
|
+
.using_lifetime(:singleton)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def load_basic_dependencies
|
76
|
+
@chain_factory = @container.resolve(:chain_factory)
|
77
|
+
@view_factory = @container.resolve(:view_factory)
|
78
|
+
@dependency_loader = @container.resolve(:dependency_loader)
|
79
|
+
@view_builder_registry = @container.resolve(:view_builder_registry)
|
80
|
+
@route_parser = @container.resolve(:route_parser)
|
81
|
+
|
82
|
+
@dependency_loader.load_components
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_routes
|
86
|
+
routes = @route_parser.parse
|
87
|
+
@route_matcher = Routing::Matcher.new(routes)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Silicon
|
2
|
+
class HandleErrors
|
3
|
+
def initialize(container, silicon_error)
|
4
|
+
@container = container
|
5
|
+
@silicon_error = silicon_error
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
def set_http_status(status_code)
|
10
|
+
@container
|
11
|
+
.register_instance(status_code, :silicon_status)
|
12
|
+
.using_lifetime(:scope)
|
13
|
+
.bound_to(:silicon_request)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module Silicon
|
4
|
+
class Chain
|
5
|
+
def initialize(container, silicon_config, commands, catch_command = nil)
|
6
|
+
@commands = commands
|
7
|
+
@container = container
|
8
|
+
@catch_command = catch_command
|
9
|
+
@async_timeout = silicon_config[:action][:async_timeout]
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
if @catch_command.nil?
|
14
|
+
run_actions
|
15
|
+
else
|
16
|
+
begin
|
17
|
+
run_actions
|
18
|
+
rescue Exception => error
|
19
|
+
@container
|
20
|
+
.register_instance(error, :silicon_error)
|
21
|
+
.using_lifetime(:scope)
|
22
|
+
.bound_to(:silicon_request)
|
23
|
+
|
24
|
+
action = @container.resolve(@catch_command.name.to_sym)
|
25
|
+
action.call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def run_actions
|
33
|
+
threads = []
|
34
|
+
Thread.abort_on_exception = true
|
35
|
+
@commands.each_with_index do |command, index|
|
36
|
+
if command.sequential?
|
37
|
+
# all previous parallel commands should be done before the current sequential
|
38
|
+
threads.each {|t| t.join}
|
39
|
+
threads = []
|
40
|
+
|
41
|
+
run_action(command.name)
|
42
|
+
else
|
43
|
+
thread = Thread.new {
|
44
|
+
Timeout::timeout(@async_timeout) {
|
45
|
+
run_action(command.name)
|
46
|
+
}
|
47
|
+
}
|
48
|
+
threads << thread if command.parallel?
|
49
|
+
end
|
50
|
+
|
51
|
+
# if it's last item in chain then wait until parallel commands are done
|
52
|
+
if index == @commands.length - 1
|
53
|
+
threads.each {|t| t.join}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def run_action(name)
|
59
|
+
action = @container.resolve(name.to_sym)
|
60
|
+
result = action.call
|
61
|
+
result_name = action.respond_to?(:result_name) ? action.result_name : "#{name}_result"
|
62
|
+
|
63
|
+
@container
|
64
|
+
.register_instance(result, result_name.to_sym)
|
65
|
+
.using_lifetime(:scope)
|
66
|
+
.bound_to(:silicon_request)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'silicon/chain'
|
2
|
+
|
3
|
+
module Silicon
|
4
|
+
class ChainFactory
|
5
|
+
def initialize(container, silicon_config)
|
6
|
+
@container = container
|
7
|
+
@silicon_config = silicon_config
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(match)
|
11
|
+
status = match.route.http_status || default_http_status(match.route.http_verb)
|
12
|
+
register_params(match.params)
|
13
|
+
register_status(status)
|
14
|
+
Chain.new(@container, @silicon_config, match.route.commands, match.route.catch)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def register_params(params)
|
20
|
+
params.keys.each do |key|
|
21
|
+
@container
|
22
|
+
.register_instance(params[key], key.to_sym)
|
23
|
+
.using_lifetime(:scope)
|
24
|
+
.bound_to(:silicon_request)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def register_status(status)
|
29
|
+
@container
|
30
|
+
.register_instance(status, :silicon_status)
|
31
|
+
.using_lifetime(:scope)
|
32
|
+
.bound_to(:silicon_request)
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_http_status(http_verb)
|
36
|
+
http_verb == 'POST' ? 201 : 200
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'silicon/extensions/hash'
|
3
|
+
|
4
|
+
module Silicon
|
5
|
+
class Config
|
6
|
+
def initialize(config_file_path)
|
7
|
+
path = File.join(Dir.pwd, config_file_path)
|
8
|
+
@params = YAML.load_file(path).deep_symbolize_keys!
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
@params[key]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Hash
|
2
|
+
# simplecov incorrectly covers activesupport variants of these overloads.
|
3
|
+
# Actually both methods are covered by unit tests.
|
4
|
+
|
5
|
+
# :nocov:
|
6
|
+
def symbolize_keys
|
7
|
+
Hash[map{|(k,v)| [k.to_sym,v]}]
|
8
|
+
end
|
9
|
+
|
10
|
+
def deep_symbolize_keys!
|
11
|
+
keys.each do |key|
|
12
|
+
val = delete(key)
|
13
|
+
self[(key.to_sym rescue key)] = val.is_a?(Hash) || val.is_a?(Array) ? val.deep_symbolize_keys! : val
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
# :nocov:
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Silicon
|
4
|
+
class CLI < Thor
|
5
|
+
include Thor::Actions
|
6
|
+
|
7
|
+
def self.source_root
|
8
|
+
File.dirname(__FILE__)
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'new NAME', 'create new Silicon application'
|
12
|
+
def new(name)
|
13
|
+
copy_file 'templates/app.rb', "#{name}/app/app.rb"
|
14
|
+
copy_file 'templates/app.routes', "#{name}/app/app.routes"
|
15
|
+
copy_file 'templates/silicon.yml', "#{name}/silicon.yml"
|
16
|
+
copy_file 'templates/config.ru', "#{name}/config.ru"
|
17
|
+
copy_file 'templates/views/show_welcome.json.jbuilder', "#{name}/app/views/show_welcome.json.jbuilder"
|
18
|
+
copy_file 'templates/actions/common/handle_errors.rb', "#{name}/app/actions/common/handle_errors.rb"
|
19
|
+
template 'templates/actions/welcome.tt', "#{name}/app/actions/welcome.rb", {app_name: name}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
json.message @message
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Silicon
|
2
|
+
class DependencyLoader
|
3
|
+
def initialize(container, type_loader)
|
4
|
+
@container = container
|
5
|
+
@types = type_loader.load_types
|
6
|
+
end
|
7
|
+
|
8
|
+
def load_components
|
9
|
+
@types.each do |type|
|
10
|
+
@container.register(type).using_lifetime(:scope).bound_to(:silicon_request)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Silicon
|
2
|
+
class TemplateLoader
|
3
|
+
def initialize(silicon_config)
|
4
|
+
@directory = File.join(silicon_config[:path][:views], '**/*')
|
5
|
+
end
|
6
|
+
|
7
|
+
def load_templates
|
8
|
+
result = {}
|
9
|
+
|
10
|
+
files = Dir.glob(@directory).reject {|file_path| File.directory? file_path}
|
11
|
+
files.each do |file|
|
12
|
+
path = File.join Dir.pwd, file
|
13
|
+
content = File.read(path)
|
14
|
+
result[file] = content
|
15
|
+
end
|
16
|
+
|
17
|
+
result
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Silicon
|
2
|
+
class TypeLoader
|
3
|
+
def initialize(silicon_config)
|
4
|
+
@directories = silicon_config[:path][:dependencies]
|
5
|
+
end
|
6
|
+
|
7
|
+
def load_types
|
8
|
+
types = []
|
9
|
+
@directories.each do |directory|
|
10
|
+
dir = File.join(directory, '**/*')
|
11
|
+
files = Dir.glob(dir).reject {|file_path| File.directory? file_path}
|
12
|
+
|
13
|
+
files.each do |file|
|
14
|
+
path = File.join Dir.pwd, file
|
15
|
+
require path
|
16
|
+
file_name = File.basename(file).gsub(File.extname(file), '')
|
17
|
+
class_name = file_name.split('_').each(&:capitalize!).join('')
|
18
|
+
types << Object.const_get(class_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
types
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|