rapid_runty 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45302d9a8dbbf57572216e3d7decd9a79504fa51
4
- data.tar.gz: 71527a5d37ac8a023a374bfb5b354738f8f61b7a
3
+ metadata.gz: c8e4a685ba91fc4049a3a5382aa56eb5923ebf10
4
+ data.tar.gz: 10d2e2bb3caf6f36d0ae842b83fc63d0a464035b
5
5
  SHA512:
6
- metadata.gz: b0b02e994bb9479b459d5f15aba669b263887508d74cf71c2ba7a08dfe5f7fe707132f1110e21ea981f50515c85f4ee6103afd6904e55333ef68791ee1d713f5
7
- data.tar.gz: 7d63588dc32c587faed2722be2f2bf547c6990c3d33d22b135f6b9b7d4a693ab9c7d11c1f242cc3db3f4095225c724ad62b6294a43f2486ac6fb53a5eda0eb3c
6
+ metadata.gz: 568008def90b41b16a42e9bec335be81811f2d5775e6337529812e820ab016fcc6377d072c2a229abeabf072034f1809fdb534865853999846ef3cf93d487b17
7
+ data.tar.gz: b09e7e72310f766892f19c7675eced90c7ffe7232b7407b67f96412f9c8ff34b9a0bac36379ccda6cc011faf0af96fcd5faa95464f14d3a6b2889acf286f655d
data/Gemfile CHANGED
@@ -3,5 +3,6 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :development, :test do
6
+ gem 'haml'
6
7
  gem 'poltergeist', git: 'git://github.com/teampoltergeist/poltergeist.git'
7
8
  end
@@ -1,5 +1,5 @@
1
- require 'rapid_runty/routing'
2
- require 'rapid_runty/controller'
1
+ require 'rapid_runty/router/base_route'
2
+ require 'rapid_runty/controller/base_controller'
3
3
 
4
4
  module RapidRunty
5
5
  ##
@@ -18,21 +18,13 @@ module RapidRunty
18
18
  #
19
19
  # @param env [Hash] Rack environment Hash that includes CGI-like headers
20
20
  #
21
- # @return [status, {headers}, [response]] array
21
+ # @return [status, {headers}, [response]]
22
22
  def call(env)
23
23
  request = Rack::Request.new(env)
24
+ response = Rack::Response.new
24
25
 
25
- verb = request.request_method.downcase.to_sym
26
- path = Rack::Utils.unescape(request.path_info)
27
-
28
- return [500, {}, []] if path == '/favicon.ico'
29
-
30
- route = routes.match(verb, path)
31
- if route.nil?
32
- [404, { 'Content-Type' => 'text/html' }, '404 not found']
33
- else
34
- [200, { 'Content-Type' => 'text/html' }, ["Hello RapidRunty"]]
35
- end
26
+ handle(env, request, response)
27
+ response.finish
36
28
  end
37
29
  end
38
30
  end
@@ -0,0 +1,66 @@
1
+ module RapidRunty
2
+ ##
3
+ # Application base controller
4
+ class BaseController
5
+ attr_reader :env, :response
6
+
7
+ def initialize(env, response)
8
+ @env = env
9
+ @response = response
10
+ end
11
+
12
+ ##
13
+ # Fetch request params
14
+ #
15
+ # @return [Hash] Hash of url parameters
16
+ def params
17
+ @params ||= Rack::Utils.parse_nested_query(env['QUERY_STRING'])
18
+ end
19
+
20
+ ##
21
+ # Render the template with a default layout.
22
+ #
23
+ # @param [String] file name for the template
24
+ def render(view = controller_action)
25
+ render_template(layout) do
26
+ render_template(view)
27
+ end
28
+ end
29
+
30
+ ##
31
+ # Tilt method to render specific template
32
+ #
33
+ # @return Rack::Response compatible response [body, status, header]
34
+ def render_template(path, &block)
35
+ Tilt.new(file(path)).render(self, &block)
36
+ end
37
+
38
+ ##
39
+ # Find template file
40
+ #
41
+ # @return [Path] template path
42
+ def file(path)
43
+ Dir[File.join(ROOT_DIR, 'app', 'views', "#{path}.html.*")].first
44
+ end
45
+
46
+ ##
47
+ # Define Layout template location
48
+ #
49
+ # @return [Path] the layout template location
50
+ def layout
51
+ File.join('layouts', 'application')
52
+ end
53
+
54
+ ##
55
+ # Redirect response method
56
+ def redirect_to(path)
57
+ response.redirect path
58
+ end
59
+
60
+ private
61
+
62
+ def controller_action
63
+ File.join(env['controller'], env['action'])
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,68 @@
1
+ require 'rapid_runty/router/routes'
2
+
3
+ module RapidRunty
4
+ class Application
5
+ attr_reader :routes
6
+
7
+ def initialize
8
+ @routes = RapidRunty::Router::Routes.new
9
+ end
10
+
11
+ # Core response method. Process the request and return the correct
12
+ # response or status message.
13
+ #
14
+ # @param env
15
+ # @param [Rack::Request] request
16
+ # @param [Rack::Response] response
17
+ def handle(env, request, response)
18
+ verb, path = route_args(request).values
19
+
20
+ route = routes.find_route(verb, path)
21
+ if route.nil?
22
+ not_found(response, path)
23
+ else
24
+ param = "&#{Rack::Utils.build_nested_query(route.placeholders)}"
25
+ env['QUERY_STRING'] << param
26
+ env.merge!(route.options)
27
+ response.write dispatch(env, route, response)
28
+ end
29
+ end
30
+
31
+ ##
32
+ # Dispatch the Controller and it's action to be rendered
33
+ def dispatch(env, route, response)
34
+ kontroller, action = route.options.values
35
+
36
+ controller = Object.const_get("#{kontroller.camel_case}Controller")
37
+ controller.new(env, response).public_send(action)
38
+ end
39
+
40
+ ##
41
+ # Default 404 error
42
+ #
43
+ # @param [Rack::Response]
44
+ #
45
+ # @return [Rack::Response]
46
+ def not_found(response, path)
47
+ response.status = 404
48
+ response.write "
49
+ <html>
50
+ <head>
51
+ <body>
52
+ <h1>404 Page not found for #{path}</h1>
53
+ </body>
54
+ </head>
55
+ </html>
56
+ "
57
+ end
58
+
59
+ private
60
+
61
+ def route_args(request)
62
+ {
63
+ verb: request.request_method.downcase.to_sym,
64
+ path: Rack::Utils.unescape(request.path_info)
65
+ }
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,50 @@
1
+ require 'rapid_runty/router/route_parser'
2
+
3
+ module RapidRunty
4
+ module Router
5
+ ##
6
+ # Matches passed in path with array of application paths
7
+ class Matcher
8
+ ##
9
+ # Defines the route that matches the path
10
+ #
11
+ # Example:
12
+ #
13
+ # RapidRunty::Router::Matcher.new.match("/foo", [{url: "/", to: "root#index"}, {url: "/bar", to: "bar#index"}, {url: "/foo", to: "foo#index"}]) #=> ["/foo", [], { controller: "foo", action: "index" }]
14
+ # RapidRunty::Router::Matcher.new.match("/04/01/01", [{url: "/:day/:month/:year", to: "date#find"}]) #=> ["/", ["04", "01", "01"], { controller: "date", action: "find" }]
15
+ # @param path [String] path from ENV["PATH_INFO"]
16
+ # @param application routes [Array] Array of Hash application defined routes
17
+ #
18
+ # Currently only supporting ":to" options which defines the "controller#action"
19
+ #
20
+ # @return [matching_route, matched_placeholders, matched_controller_action] array
21
+ #
22
+ def match(path, routes)
23
+ path = route_parser.new(path)
24
+ url_patterns = routes.map { |route| route_parser.new(route) }
25
+
26
+ url_patterns.each do |pattern|
27
+ return [
28
+ pattern.to_s,
29
+ pattern.placeholders,
30
+ controller_action(pattern.options)
31
+ ] if pattern == path
32
+ end
33
+
34
+ [nil, {}, {}]
35
+ end
36
+
37
+ def controller_action(options)
38
+ Hash[
39
+ %w(controller action).zip options.split('#')
40
+ ]
41
+ end
42
+
43
+ private
44
+
45
+ def route_parser
46
+ RapidRunty::Router::RouteParser
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,16 @@
1
+ module RapidRunty
2
+ module Router
3
+ ##
4
+ # Defines a single user defined route.
5
+ class Route
6
+ attr_accessor :verb, :path, :options, :placeholders
7
+
8
+ def initialize(verb, path, options)
9
+ self.verb = verb
10
+ self.path = path
11
+ self.options = options
12
+ self.placeholders = nil
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,62 @@
1
+ module RapidRunty
2
+ module Router
3
+ ##
4
+ # Parses the route, extracts and return the various important parts of the url.
5
+ #
6
+ # @param [String] the request url
7
+ class RouteParser
8
+ attr_accessor :path, :options
9
+
10
+ def initialize(url)
11
+ self.path = split_url(url[:url])
12
+ self.options = url[:to]
13
+ rescue TypeError
14
+ self.path = split_url(url)
15
+ end
16
+
17
+ def to_s
18
+ '/' + path.join('/')
19
+ end
20
+
21
+ ##
22
+ # Return the present placeholders for the url
23
+ #
24
+ # @return [Hash] of the placeholder key and its value.
25
+ def placeholders
26
+ return {} unless @match
27
+
28
+ placeholders = {}
29
+ path.each_with_index do |part, i|
30
+ placeholders[part.delete(':').to_s] = @match.path[i] if part[0] == ':'
31
+ end
32
+ placeholders
33
+ end
34
+
35
+ def ==(other)
36
+ is_match = size_match?(other) && path_match?(other)
37
+ @match = other if is_match
38
+ is_match
39
+ end
40
+
41
+ private
42
+
43
+ def size_match?(url)
44
+ path.size == url.path.size
45
+ end
46
+
47
+ def path_match?(url)
48
+ path.each_with_index do |part, i|
49
+ return false unless part[0] == ':' || url.path[i] == part
50
+ end
51
+ true
52
+ end
53
+
54
+ def split_url(url)
55
+ url = url.to_s
56
+ path_parts = url.split('/').reject(&:empty?)
57
+ path_parts = [''] if url == '/'
58
+ path_parts
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,4 +1,6 @@
1
- require 'rapid_runty/routes/matcher'
1
+ require 'rapid_runty/router/route_parser'
2
+ require 'rapid_runty/router/route'
3
+ require 'rapid_runty/router/matcher'
2
4
 
3
5
  module RapidRunty
4
6
  module Router
@@ -6,7 +8,7 @@ module RapidRunty
6
8
  # This class registers all routes defined by the draw method.
7
9
  class Routes < Array
8
10
  def add(*args)
9
- self << Route.new(*args)
11
+ self << RapidRunty::Router::Route.new(*args)
10
12
  end
11
13
 
12
14
  ##
@@ -18,21 +20,30 @@ module RapidRunty
18
20
  # @return A RapidRunty::Router::Route Instance:
19
21
  #
20
22
  # Example:
21
- # RapidRunty::Router::Routes.match("GET", "/foo/4") = #<RapidRunty::Router::Route options={"controller"=>"foo", "action"=>"bar"}, path="/foo/:id", placeholders=[], verb=:get
22
- def match(verb, path)
23
+ # RapidRunty::Router::Routes.find_route("GET", "/foo/4") => #<RapidRunty::Router::Route options={"controller"=>"foo", "action"=>"bar"}, path="/foo/:id", placeholders=[], verb=:get
24
+ def find_route(verb, path)
23
25
  return nil if empty?
24
26
 
25
27
  verb = verb.to_s.downcase.strip.to_sym
26
-
27
28
  routes = select { |route| route.verb == verb }
28
- paths = routes.map { |route| { url: route.path }.merge route.options }
29
29
 
30
- path, places, action = RapidRunty::Router::Matcher.match(path, paths)
31
- return nil if path.nil?
30
+ urls = select_routes_by_verb(verb, routes)
31
+
32
+ find_matching_route(path, urls, routes)
33
+ end
34
+
35
+ def select_routes_by_verb(_verb, routes)
36
+ urls = routes.map { |route| { url: route.path }.merge route.options }
37
+ urls
38
+ end
39
+
40
+ def find_matching_route(path, urls, routes)
41
+ url, placeholders, controller_action = RapidRunty::Router::Matcher.new.match(path, urls)
42
+ return nil if url.nil?
32
43
 
33
- route = routes.detect { |router| router.path == path }.clone
34
- route.placeholders = places
35
- route.options = action
44
+ route = routes.detect { |router| router.path == url }.clone
45
+ route.placeholders = placeholders
46
+ route.options = controller_action
36
47
  route
37
48
  end
38
49
 
@@ -65,16 +76,5 @@ module RapidRunty
65
76
  end
66
77
  end
67
78
  end
68
-
69
- class Route
70
- attr_accessor :verb, :path, :options, :placeholders
71
-
72
- def initialize(verb, path, options)
73
- self.verb = verb
74
- self.path = path
75
- self.options = options
76
- self.placeholders = nil
77
- end
78
- end
79
79
  end
80
80
  end
@@ -14,4 +14,14 @@ class String
14
14
  downcase!
15
15
  self
16
16
  end
17
+
18
+ ##
19
+ # Returns the CamelCase version of a word
20
+ #
21
+ # Example:
22
+ # "index_controller".camel_case = "IndexController"
23
+ def camel_case
24
+ return self if self !~ /_/ && self =~ /[A-Z]+.*/
25
+ split('_').map(&:capitalize).join
26
+ end
17
27
  end
@@ -1,3 +1,3 @@
1
1
  module RapidRunty
2
- VERSION = '0.1.2'.freeze
2
+ VERSION = '0.1.3'.freeze
3
3
  end
data/rapid_runty.gemspec CHANGED
@@ -42,4 +42,6 @@ Gem::Specification.new do |spec|
42
42
  spec.add_dependency 'rack', '~> 2.0'
43
43
  spec.add_dependency 'bundler', '~> 1.12'
44
44
  spec.add_dependency 'thor', '~> 0.19'
45
+ spec.add_dependency 'tilt', '~> 2.0'
46
+ spec.add_dependency 'sqlite3', '~> 1.3'
45
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rapid_runty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Herbert Kagumba
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-08-20 00:00:00.000000000 Z
11
+ date: 2016-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -234,6 +234,34 @@ dependencies:
234
234
  - - "~>"
235
235
  - !ruby/object:Gem::Version
236
236
  version: '0.19'
237
+ - !ruby/object:Gem::Dependency
238
+ name: tilt
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: '2.0'
244
+ type: :runtime
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - "~>"
249
+ - !ruby/object:Gem::Version
250
+ version: '2.0'
251
+ - !ruby/object:Gem::Dependency
252
+ name: sqlite3
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - "~>"
256
+ - !ruby/object:Gem::Version
257
+ version: '1.3'
258
+ type: :runtime
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - "~>"
263
+ - !ruby/object:Gem::Version
264
+ version: '1.3'
237
265
  description: A minimal web framework to get your web project up and running in seconds.
238
266
  email:
239
267
  - herbert.kagumba@andela.com
@@ -259,11 +287,13 @@ files:
259
287
  - gemify.sh
260
288
  - lib/rapid_runty.rb
261
289
  - lib/rapid_runty/application.rb
262
- - lib/rapid_runty/controller.rb
290
+ - lib/rapid_runty/controller/base_controller.rb
263
291
  - lib/rapid_runty/dependencies.rb
264
- - lib/rapid_runty/routes/matcher.rb
265
- - lib/rapid_runty/routes/route.rb
266
- - lib/rapid_runty/routing.rb
292
+ - lib/rapid_runty/router/base_route.rb
293
+ - lib/rapid_runty/router/matcher.rb
294
+ - lib/rapid_runty/router/route.rb
295
+ - lib/rapid_runty/router/route_parser.rb
296
+ - lib/rapid_runty/router/routes.rb
267
297
  - lib/rapid_runty/util.rb
268
298
  - lib/rapid_runty/version.rb
269
299
  - rapid_runty.gemspec
@@ -1,12 +0,0 @@
1
- module RapidRunty
2
- # Application controller
3
- #
4
- # TODO: Just saves the env for now
5
- class Controller
6
- def initialize(env)
7
- @env = env
8
- end
9
-
10
- attr_reader :env
11
- end
12
- end
@@ -1,110 +0,0 @@
1
- require 'pathname'
2
-
3
- module RapidRunty
4
- module Router
5
- ##
6
- # Matches passed in path with array of application paths
7
- module Matcher
8
- def self.match(*args)
9
- Base.match(*args)
10
- end
11
-
12
- class Base
13
- ##
14
- # Defines the route that matches the path
15
- #
16
- # Example:
17
- #
18
- # RapidRunty::Router::Matcher.match("/foo", [{url: "/", to: "root#index"}, {url: "/bar", to: "bar#index"}, {url: "/foo", to: "foo#index"}]) #=> ["/foo", [], { controller: "foo", action: "index" }]
19
- # RapidRunty::Router::Matcher.match("/04/01/01", [{url: "/:day/:month/:year", to: "date#find"}]) #=> ["/", ["04", "01", "01"], { controller: "date", action: "find" }]
20
- # @param path [String] path from ENV["PATH_INFO"]
21
- # @param application routes [Array] Array of Hash application defined routes
22
- #
23
- # Currently only supporting ":to" options which defines the "controller#action"
24
- #
25
- # @return [matching_route, matched_placeholders, matched_controller_action] array
26
- #
27
- def self.match(path, routes)
28
- path = Path.new(path)
29
- url_patterns = routes.map { |route| URLPattern.new(route) }
30
-
31
- url_patterns.each do |pattern|
32
- return [
33
- pattern.to_s,
34
- pattern.placeholders,
35
- ControllerSetup.controller_action(pattern.options)
36
- ] if pattern == path
37
- end
38
-
39
- [nil, [], {}]
40
- end
41
- end
42
-
43
- class Path
44
- attr_accessor :parts, :ext, :options
45
-
46
- def initialize(path)
47
- path = Pathname(path)
48
- self.parts, self.ext = split_path(path)
49
- rescue TypeError
50
- self.parts, self.ext = split_path(path[:url])
51
- self.options = path[:to]
52
- end
53
-
54
- def to_s
55
- '/' + parts.join('/') + ext
56
- end
57
-
58
- private
59
-
60
- def split_path(path)
61
- path = path.to_s
62
- ext = Pathname(path).extname
63
- path = path.sub(/#{ext}$/, '')
64
- parts = path.split('/').reject(&:empty?)
65
- parts = [''] if path == '/'
66
- [parts, ext]
67
- end
68
- end
69
-
70
- class URLPattern < Path
71
- def placeholders
72
- return [] unless @match
73
-
74
- vars = []
75
- parts.each_with_index do |part, i|
76
- vars << @match.parts[i] if part[0] == ':'
77
- end
78
- vars
79
- end
80
-
81
- def ==(other)
82
- is_match = size_match?(other) && parts_match?(other)
83
- @match = other if is_match
84
- is_match
85
- end
86
-
87
- private
88
-
89
- def size_match?(path)
90
- parts.size == path.parts.size
91
- end
92
-
93
- def parts_match?(path)
94
- parts.each_with_index do |part, i|
95
- return true if part[0] == ':' || path.parts[i] == part
96
- end
97
- false
98
- end
99
- end
100
-
101
- class ControllerSetup
102
- def self.controller_action(options)
103
- Hash[
104
- %w(controller action).zip options.split('#')
105
- ]
106
- end
107
- end
108
- end
109
- end
110
- end
@@ -1,11 +0,0 @@
1
- require 'rapid_runty/routes/route'
2
-
3
- module RapidRunty
4
- class Application
5
- attr_reader :routes
6
-
7
- def initialize
8
- @routes = RapidRunty::Router::Routes.new
9
- end
10
- end
11
- end