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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +372 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +7 -0
  10. data/bin/setup +8 -0
  11. data/exe/silicon +5 -0
  12. data/lib/silicon.rb +6 -0
  13. data/lib/silicon/app.rb +90 -0
  14. data/lib/silicon/base/handle_errors.rb +16 -0
  15. data/lib/silicon/chain.rb +69 -0
  16. data/lib/silicon/chain_factory.rb +39 -0
  17. data/lib/silicon/config.rb +15 -0
  18. data/lib/silicon/errors/silicon_error.rb +4 -0
  19. data/lib/silicon/errors/syntax_error.rb +6 -0
  20. data/lib/silicon/errors/view_engine_error.rb +6 -0
  21. data/lib/silicon/extensions/hash.rb +18 -0
  22. data/lib/silicon/generators/cli.rb +23 -0
  23. data/lib/silicon/generators/templates/actions/common/handle_errors.rb +2 -0
  24. data/lib/silicon/generators/templates/actions/welcome.tt +9 -0
  25. data/lib/silicon/generators/templates/app.rb +4 -0
  26. data/lib/silicon/generators/templates/app.routes +4 -0
  27. data/lib/silicon/generators/templates/config.ru +8 -0
  28. data/lib/silicon/generators/templates/silicon.yml +7 -0
  29. data/lib/silicon/generators/templates/views/show_welcome.json.jbuilder +1 -0
  30. data/lib/silicon/loaders/dependency_loader.rb +14 -0
  31. data/lib/silicon/loaders/template_loader.rb +20 -0
  32. data/lib/silicon/loaders/type_loader.rb +25 -0
  33. data/lib/silicon/request.rb +65 -0
  34. data/lib/silicon/routing/builder.rb +96 -0
  35. data/lib/silicon/routing/file_reader.rb +27 -0
  36. data/lib/silicon/routing/match.rb +12 -0
  37. data/lib/silicon/routing/matcher.rb +40 -0
  38. data/lib/silicon/routing/parser.rb +25 -0
  39. data/lib/silicon/routing/route.rb +19 -0
  40. data/lib/silicon/routing/routing.rb +11 -0
  41. data/lib/silicon/routing/syntax.rb +28 -0
  42. data/lib/silicon/routing/syntax/action.rb +30 -0
  43. data/lib/silicon/routing/syntax/actions.rb +15 -0
  44. data/lib/silicon/routing/syntax/after_section.rb +19 -0
  45. data/lib/silicon/routing/syntax/before_section.rb +18 -0
  46. data/lib/silicon/routing/syntax/catch_section.rb +15 -0
  47. data/lib/silicon/routing/syntax/command.rb +27 -0
  48. data/lib/silicon/routing/syntax/commands.rb +7 -0
  49. data/lib/silicon/routing/syntax/node.rb +47 -0
  50. data/lib/silicon/routing/syntax/nodes.rb +13 -0
  51. data/lib/silicon/routing/syntax/primitives/arrow.rb +4 -0
  52. data/lib/silicon/routing/syntax/primitives/back_arrow.rb +4 -0
  53. data/lib/silicon/routing/syntax/primitives/eol.rb +4 -0
  54. data/lib/silicon/routing/syntax/primitives/http_status.rb +13 -0
  55. data/lib/silicon/routing/syntax/primitives/http_verb.rb +4 -0
  56. data/lib/silicon/routing/syntax/primitives/indent.rb +4 -0
  57. data/lib/silicon/routing/syntax/primitives/parameter.rb +4 -0
  58. data/lib/silicon/routing/syntax/primitives/path.rb +5 -0
  59. data/lib/silicon/routing/syntax/respond.rb +25 -0
  60. data/lib/silicon/routing/syntax/route.rb +25 -0
  61. data/lib/silicon/routing/syntax/sections.rb +19 -0
  62. data/lib/silicon/routing/syntax/tree_section.rb +11 -0
  63. data/lib/silicon/routing/syntax/view.rb +7 -0
  64. data/lib/silicon/routing/syntax_error_interpreter.rb +28 -0
  65. data/lib/silicon/routing/syntax_grammar.tt +95 -0
  66. data/lib/silicon/template_registry.rb +17 -0
  67. data/lib/silicon/version.rb +3 -0
  68. data/lib/silicon/view_builder.rb +18 -0
  69. data/lib/silicon/view_builder_registry.rb +21 -0
  70. data/lib/silicon/view_builders/json.rb +16 -0
  71. data/lib/silicon/view_factory.rb +21 -0
  72. data/silicon.gemspec +45 -0
  73. metadata +272 -0
@@ -0,0 +1,65 @@
1
+ require 'silicon/chain_factory'
2
+ require 'silicon/view_factory'
3
+
4
+ module Silicon
5
+ class Request
6
+ include Hypo::Scope
7
+
8
+ def initialize(route_matcher, container, chain_factory, view_factory)
9
+ @container = container
10
+ @route_matcher = route_matcher
11
+ @chain_factory = chain_factory
12
+ @view_factory = view_factory
13
+ end
14
+
15
+ def handle(rack_env)
16
+ create_scope
17
+
18
+ path = rack_env['PATH_INFO']
19
+ request_method = rack_env['REQUEST_METHOD']
20
+ match = @route_matcher.match(path, request_method)
21
+ content_type = rack_env['CONTENT_TYPE'] || 'application/json'
22
+
23
+ if match.nil?
24
+ result = [404, { 'Content-Type' => content_type }, []]
25
+ else
26
+ query = Rack::Utils.parse_nested_query(rack_env['QUERY_STRING']).symbolize_keys
27
+ register_params(query, :silicon_query)
28
+
29
+ data = rack_env['rack.parser.result']
30
+ register_params(data, :silicon_data)
31
+
32
+ chain = @chain_factory.create(match)
33
+ chain.execute
34
+
35
+ body = ''
36
+ if match.route.view
37
+ body = @view_factory.create(match.route.view, content_type)
38
+ end
39
+
40
+ status = @container.resolve(:silicon_status)
41
+ result = [status, { 'Content-Type' => content_type }, [body]]
42
+ end
43
+
44
+ release
45
+
46
+ result
47
+ end
48
+
49
+ private
50
+ def create_scope
51
+ @container
52
+ .register_instance(self, :silicon_request)
53
+ .using_lifetime(:scope)
54
+ .bound_to(self)
55
+ end
56
+
57
+ def register_params(params, name)
58
+ unless params.nil?
59
+ @container.register_instance(params, name)
60
+ .using_lifetime(:scope)
61
+ .bound_to(:silicon_request)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,96 @@
1
+ require 'silicon/routing/route'
2
+
3
+ module Silicon
4
+ module Routing
5
+ class Builder
6
+ def initialize
7
+ @parsed_items = []
8
+ @route_params = []
9
+ @current_parent = nil
10
+ @prev_route = nil
11
+ end
12
+
13
+ def build(sections)
14
+ result = []
15
+
16
+ node = sections.node
17
+ restore_hierarchy(node, nil,node.to_hash)
18
+
19
+ @route_params.each do |route|
20
+ if route[:actions]
21
+ route[:actions].each do |action|
22
+ result << Route.new({
23
+ path: restore_path(route).gsub('$', '/$'),
24
+ params: restore_params(route).map{|p| p.sub('$', '')},
25
+ http_verb: action[:http_verb],
26
+ view: action[:view],
27
+ http_status: action[:http_status],
28
+ catch: sections.catch.command,
29
+ commands: restore_callbacks(route, :before) + action[:commands] + restore_callbacks(route, :after)
30
+ })
31
+ end
32
+ end
33
+ end
34
+
35
+ result
36
+ end
37
+
38
+ private
39
+
40
+ def restore_hierarchy(node, prev_route, current_parent)
41
+ route = node.to_hash
42
+
43
+ if route[:level] == current_parent[:level]
44
+ route[:parent] = current_parent[:parent]
45
+ parent = route
46
+ elsif route[:level] > prev_route[:level]
47
+ route[:parent] = prev_route
48
+ parent = prev_route
49
+ else
50
+ route[:parent] = current_parent
51
+ parent = current_parent
52
+ end
53
+
54
+ @route_params << route
55
+ @prev_route = route
56
+
57
+ if node.my_nodes
58
+ node.my_nodes.each {|n| restore_hierarchy(n, route, parent)}
59
+ end
60
+ end
61
+
62
+ def restore_path(route)
63
+ path = (route[:route][:path] || route[:route][:parameter] || '')
64
+ if route[:parent]
65
+ restore_path(route[:parent]) + path
66
+ else
67
+ path
68
+ end
69
+ end
70
+
71
+ def restore_params(route)
72
+ if route[:route][:parameter]
73
+ param = [route[:route][:parameter]]
74
+ else
75
+ param = []
76
+ end
77
+
78
+ if route[:parent]
79
+ restore_params(route[:parent]) + param
80
+ else
81
+ param
82
+ end
83
+ end
84
+
85
+ def restore_callbacks(route, type)
86
+ before = route[type]
87
+
88
+ if route[:parent]
89
+ restore_callbacks(route[:parent], type) + before
90
+ else
91
+ before
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,27 @@
1
+ module Silicon
2
+ module Routing
3
+ class FileReader
4
+ def initialize(silicon_config)
5
+ @config = silicon_config
6
+ end
7
+
8
+ def read
9
+ path = File.join('./', @config[:path][:routes])
10
+ content = File.read(path)
11
+
12
+ # use '^' instead spaces and tabs
13
+ raw_content = content.gsub(/\t/, ' ')
14
+ .gsub(' ', '^')
15
+ .gsub(' ', '')
16
+
17
+ ###Hack:
18
+ # grammar problem - can't simply match '-' character in route path when '->' present.
19
+ # Will be resolved later. Replace '->', '<-' with '*>', '<*' for a while for syntax parser.
20
+ ###
21
+ raw_content = raw_content.gsub('->', '*>').gsub('<-', '<*')
22
+
23
+ raw_content.gsub!("\n", ';') + ';'
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ module Silicon
2
+ module Routing
3
+ class Match
4
+ attr_reader :route, :params
5
+
6
+ def initialize(route, params)
7
+ @route = route
8
+ @params = params
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ require 'silicon/routing/match'
2
+
3
+ module Silicon
4
+ module Routing
5
+ class Matcher
6
+ def initialize(routes)
7
+ @routes = routes
8
+ end
9
+
10
+ def match(path, http_method)
11
+ segments = path.split('/')
12
+ params = {}
13
+ matched = nil
14
+
15
+ @routes.each do |route|
16
+ if route.segments.length == segments.length
17
+ for i in 0..segments.length - 1 do
18
+ template = route.segments[i]
19
+ value = segments[i]
20
+
21
+ if value == template
22
+ elsif template.index('$') == 0
23
+ name = template.sub('$', '')
24
+ params[name] = value
25
+ else
26
+ break
27
+ end
28
+
29
+ if i == segments.length - 1 && route.http_verb == http_method
30
+ matched = route
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ matched.nil? ? nil : Match.new(matched, params)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ module Silicon
2
+ module Routing
3
+ class Parser
4
+ def initialize(file_reader, routes_builder,
5
+ syntax_parser, syntax_error_interpreter)
6
+ @file_reader = file_reader
7
+ @routes_builder = routes_builder
8
+ @syntax_parser = syntax_parser
9
+ @syntax_error_interpreter = syntax_error_interpreter
10
+ end
11
+
12
+ def parse
13
+ content = @file_reader.read
14
+ tree = @syntax_parser.parse(content)
15
+
16
+ if tree.nil?
17
+ error_message = @syntax_error_interpreter.interpret
18
+ raise Silicon::SyntaxError, error_message
19
+ end
20
+
21
+ @routes_builder.build(tree.parse)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Silicon
2
+ module Routing
3
+ class Route
4
+ attr_reader :http_verb, :path, :params, :catch,
5
+ :commands, :segments, :view, :http_status
6
+
7
+ def initialize(hash)
8
+ @http_verb = hash[:http_verb]
9
+ @path = hash[:path].sub('.', '')
10
+ @params = hash[:params]
11
+ @commands = hash[:commands]
12
+ @view = hash[:view]
13
+ @http_status = hash[:http_status]
14
+ @segments = @path.split('/')
15
+ @catch = hash[:catch]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ require 'treetop'
2
+ require 'silicon/routing/builder'
3
+ require 'silicon/routing/parser'
4
+ require 'silicon/routing/matcher'
5
+ require 'silicon/routing/file_reader'
6
+ require 'silicon/routing/syntax_error_interpreter'
7
+ require 'silicon/routing/syntax_grammar'
8
+ require 'silicon/routing/syntax'
9
+
10
+ module Routing
11
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'syntax/primitives/arrow'
2
+ require_relative 'syntax/primitives/back_arrow'
3
+ require_relative 'syntax/primitives/eol'
4
+ require_relative 'syntax/primitives/indent'
5
+ require_relative 'syntax/primitives/http_verb'
6
+ require_relative 'syntax/primitives/http_status'
7
+ require_relative 'syntax/primitives/parameter'
8
+ require_relative 'syntax/primitives/path'
9
+
10
+ require_relative 'syntax/action'
11
+ require_relative 'syntax/actions'
12
+ require_relative 'syntax/command'
13
+ require_relative 'syntax/commands'
14
+ require_relative 'syntax/view'
15
+ require_relative 'syntax/node'
16
+ require_relative 'syntax/nodes'
17
+ require_relative 'syntax/route'
18
+ require_relative 'syntax/respond'
19
+
20
+
21
+ require_relative 'syntax/sections'
22
+ require_relative 'syntax/before_section'
23
+ require_relative 'syntax/tree_section'
24
+ require_relative 'syntax/after_section'
25
+ require_relative 'syntax/catch_section'
26
+
27
+ module Syntax
28
+ end
@@ -0,0 +1,30 @@
1
+ module Syntax
2
+ class Action < Treetop::Runtime::SyntaxNode
3
+ attr_reader :http_verb, :commands, :node, :respond
4
+
5
+ def parse(node)
6
+ @node = node
7
+ @commands = []
8
+
9
+ elements.each do |element|
10
+ if element.is_a? HttpVerb
11
+ @http_verb = element.text_value
12
+ end
13
+
14
+ if element.is_a? Commands
15
+ @commands = element.parse
16
+ end
17
+
18
+ if element.is_a? Respond
19
+ @respond = element.parse
20
+ end
21
+ end
22
+
23
+ self
24
+ end
25
+
26
+ def to_hash
27
+ {http_verb: @http_verb, commands: @commands, view: @respond.view, http_status: @respond.http_status}
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ module Syntax
2
+ class Actions < Treetop::Runtime::SyntaxNode
3
+ attr_reader :actions
4
+
5
+ def parse(node)
6
+ @actions = []
7
+
8
+ elements.each do |element|
9
+ @actions << element.parse(node).to_hash
10
+ end
11
+
12
+ @actions
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Syntax
2
+ class AfterSection < Treetop::Runtime::SyntaxNode
3
+ attr_reader :commands
4
+
5
+ def parse
6
+ @commands = []
7
+
8
+ if elements.length > 0
9
+ elements[0].elements.each do |element|
10
+ if element.is_a? Commands
11
+ @commands = element.parse
12
+ end
13
+ end
14
+ end
15
+
16
+ self
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module Syntax
2
+ class BeforeSection < Treetop::Runtime::SyntaxNode
3
+ attr_reader :commands
4
+
5
+ def parse
6
+ @commands = []
7
+ if elements.length > 0
8
+ elements[0].elements.each do |element|
9
+ if element.is_a? Commands
10
+ @commands = element.parse
11
+ end
12
+ end
13
+ end
14
+
15
+ self
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module Syntax
2
+ class CatchSection < Treetop::Runtime::SyntaxNode
3
+ attr_reader :command
4
+
5
+ def parse
6
+ elements[0].elements.each do |element|
7
+ if element.is_a? Command
8
+ @command = element.parse
9
+ end
10
+ end
11
+
12
+ self
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ module Syntax
2
+ class Command < Treetop::Runtime::SyntaxNode
3
+ attr_reader :name
4
+
5
+ def parse
6
+ @is_async = text_value.start_with? '=*'
7
+ @is_parallel = text_value.start_with? '=>'
8
+ @is_sequential = text_value.start_with? '*>'
9
+
10
+ @name = text_value.sub('*>', '').sub('=*', '').sub('=>', '')
11
+
12
+ self
13
+ end
14
+
15
+ def async?
16
+ @is_async
17
+ end
18
+
19
+ def parallel?
20
+ @is_parallel
21
+ end
22
+
23
+ def sequential?
24
+ @is_sequential
25
+ end
26
+ end
27
+ end