leanweb 0.1.3 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3068af18fa92b34c6129894552a74e184c8ebea84cab6d90066a6e004ce9cc5
4
- data.tar.gz: 4fb4f1a3361cc7d986456de08c463899338743d58abc4c8f5099919347df6d1b
3
+ metadata.gz: d4e80c5813858b838b39fea56bb96b2fb28f7749d22f02ab04694662a8595d96
4
+ data.tar.gz: c2fdc8ebf92d647516db11352e5a6cb7bc36f1489dfabe69c095e7090f1b7970
5
5
  SHA512:
6
- metadata.gz: 3896c65a6db177ac696a580ae3667ec43981969dd7654eaa87469016712cdfc022a5a64bd8fe48aed97c15e09beb009d97dd79ca85f7328a16293925f52031cf
7
- data.tar.gz: 72489e08ddfd2c08176af510c4b8cfddf6b7e4e0a8846b79173f18676d27dc63540ed359708aa679cfe351c2f9e69b75e28639de895368f97faa0a71cc3bcc2e
6
+ metadata.gz: 1bed8458a93a2f0139609f17fe3402f69065182607b6b6664cc0c6d90ab3815f4dca7cd08c165dab17e5448d9c3f7d3c44ef8e1f28468cd3e4c42a66b096c8b8
7
+ data.tar.gz: 320c619aedd70c9bd0ed5e698ef456e293d3cf754bbe3ca939a7669b45ad117a371089db15737edf0c5c271ad638b84784e6ee3a730093a82a16843828a1074c
data/bin/leanweb CHANGED
@@ -3,10 +3,10 @@
3
3
 
4
4
  # Copyright 2022 Felix Freeman <libsys@hacktivista.org>
5
5
  #
6
- # This file is part of "LeanWeb" and licensed under the terms of the Hacktivista
7
- # General Public License version 0.1 or (at your option) any later version. You
8
- # should have received a copy of this license along with the software. If not,
9
- # see <https://hacktivista.org/licenses/>.
6
+ # This file is part of "LeanWeb" and licensed under the terms of the GNU Affero
7
+ # General Public License version 3 with permissions for compatibility with the
8
+ # Hacktivista General Public License. You should have received a copy of this
9
+ # license along with the software. If not, see <https://gnu.org/licenses/>.
10
10
 
11
11
  require 'fileutils'
12
12
  require 'leanweb'
@@ -22,6 +22,9 @@ files = [
22
22
  source 'https://rubygems.org'
23
23
 
24
24
  gem 'leanweb', '~> #{LeanWeb::VERSION}'
25
+ gem 'haml', '~> 5.2.2'
26
+ gem 'rake'
27
+ gem 'puma'
25
28
  RUBY
26
29
  }, {
27
30
  filename: 'config.ru',
@@ -29,21 +32,21 @@ files = [
29
32
  # frozen_string_literal: true
30
33
 
31
34
  require 'leanweb'
35
+ require_relative 'routes'
32
36
 
33
37
  if ENV['RACK_ENV'] == 'development'
34
38
  use(Rack::Reloader, 0)
35
- use(Rack::Static, urls: [''], root: 'public', cascade: true)
39
+ use(Rack::Static, urls: ['/assets'], root: 'public', cascade: true)
36
40
  end
37
41
 
38
- app = LeanWeb::App.init
39
- run app
42
+ run LeanWeb::App.new(ROUTES)
40
43
  RUBY
41
44
  }, {
42
45
  filename: 'routes.rb',
43
46
  content: <<~RUBY
44
47
  # frozen_string_literal: true
45
48
 
46
- [{ path: '/' }]
49
+ ROUTES = [{ path: '/' }].freeze
47
50
  RUBY
48
51
  }, {
49
52
  filename: 'Rakefile',
@@ -52,23 +55,24 @@ files = [
52
55
 
53
56
  require 'leanweb'
54
57
 
55
- task default: %w[build]
58
+ task default: %w[build_static]
56
59
 
57
- task :build do
58
- LeanWeb::App.init.build_static
60
+ task :build_static do
61
+ require_relative 'routes'
62
+ LeanWeb::App.new(ROUTES).build_static
59
63
  end
60
64
  RUBY
61
65
  }, {
62
- filename: 'src/controllers/main.rb',
66
+ filename: 'src/controllers/main_controller.rb',
63
67
  content: <<~RUBY
64
68
  # frozen_string_literal: true
65
69
 
66
70
  require 'leanweb'
67
71
 
68
- # Main controller is the default controller
72
+ # Main controller is the default controller.
69
73
  class MainController < LeanWeb::Controller
70
74
  def index_get
71
- render 'index.haml'
75
+ render_response 'index.haml'
72
76
  end
73
77
  end
74
78
  RUBY
@@ -96,9 +100,10 @@ begin
96
100
  path = ARGV[1] || '.'
97
101
  FileUtils.mkdir_p(path)
98
102
  FileUtils.cd(path)
99
- FileUtils.mkdir_p(['public', 'src/controllers', 'src/views'])
100
- files.each { |file| File.write(file[:filename], file[:content]) }
103
+ FileUtils.mkdir_p(['public/assets', 'src/controllers', 'src/views'])
104
+ files.each{ |file| File.write(file[:filename], file[:content]) }
101
105
  `git init`
106
+ `bundle install`
102
107
  puts("Project '#{File.basename(Dir.getwd)}' successfully created.")
103
108
  rescue # rubocop:disable Style/RescueStandardError
104
109
  puts('Woops! Something went wrong.')
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2022 Felix Freeman <libsys@hacktivista.org>
4
+ #
5
+ # This file is part of "LeanWeb" and licensed under the terms of the GNU Affero
6
+ # General Public License version 3 with permissions for compatibility with the
7
+ # Hacktivista General Public License. You should have received a copy of this
8
+ # license along with the software. If not, see <https://gnu.org/licenses/>.
9
+
10
+ require 'json'
11
+ require 'net/http'
12
+ require 'openssl'
13
+ require 'tilt'
14
+ require 'uri'
15
+
16
+ # Hawese client.
17
+ #
18
+ # **Environment variables**
19
+ #
20
+ # - HAWESE_RETURN_URL defaults to `LEANWEB_ENDPOINT/checkout`.
21
+ # - HAWESE_ENDPOINT
22
+ # - HAWESE_ORIGIN
23
+ class Hawese
24
+ class << self
25
+ ENDPOINT = ENV.fetch('HAWESE_ENDPOINT')
26
+ RETURN_URL = ENV.fetch('HAWESE_RETURN_URL') do
27
+ "#{ENV.fetch('LEANWEB_ENDPOINT')}/checkout"
28
+ end
29
+ ORIGIN = ENV.fetch('HAWESE_ORIGIN')
30
+
31
+ def payment_methods
32
+ uri = URI("#{ENDPOINT}/gateways/payment-methods/purchase")
33
+ JSON.parse(Net::HTTP.get(uri))
34
+ end
35
+
36
+ def gateway_schema(gateway, query_params, schema = 'purchase')
37
+ uri = URI("#{ENDPOINT}/gateways/#{gateway}/schemas/#{schema}")
38
+ uri.query = URI.encode_www_form(query_params)
39
+ JSON.parse(Net::HTTP.get(uri))
40
+ end
41
+
42
+ def purchase(gateway, fields)
43
+ fields[:origin] = ORIGIN
44
+ uri = URI("#{ENDPOINT}/gateways/#{gateway}/purchase")
45
+ uri.query = URI.encode_www_form(return_url: RETURN_URL)
46
+ response = Net::HTTP.post(
47
+ uri,
48
+ fields.to_json,
49
+ 'Content-Type' => 'application/json'
50
+ )
51
+ JSON.parse(response.body)
52
+ end
53
+
54
+ def purchase_from_schema(gateway, schema)
55
+ fields = {}
56
+ schema['properties'].each do |property, contents|
57
+ fields[property] = contents['default'] if contents.include?('default')
58
+ end
59
+ purchase(gateway, fields)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2022 Felix Freeman <libsys@hacktivista.org>
4
+ #
5
+ # This file is part of "LeanWeb" and licensed under the terms of the GNU Affero
6
+ # General Public License version 3 with permissions for compatibility with the
7
+ # Hacktivista General Public License. You should have received a copy of this
8
+ # license along with the software. If not, see <https://gnu.org/licenses/>.
9
+
10
+ require 'net/smtp'
11
+ require 'securerandom'
12
+ require 'socket'
13
+ require 'time'
14
+
15
+ # Send an email with SMTP easily.
16
+ #
17
+ # Environment variables:
18
+ #
19
+ # - SMTP_HOST: Where to connect to.
20
+ # - SMTP_PORT: Port, optional (default: 25).
21
+ # - SMTP_SECURITY: `tls` or `starttls`, optional (default: `nil`).
22
+ # - SMTP_USER: User, optional.
23
+ # - SMTP_PASSWORD: Password, optional.
24
+ # - SMTP_FROM: In the format `Name <user@mail>` or `user@mail`.
25
+ #
26
+ # @example Single address
27
+ # LeanMail.deliver('to@mail', 'subject', 'body')
28
+ #
29
+ # @example Multiple addresses
30
+ # LeanMail.deliver(['to@mail', 'to2@mail'], 'subject', 'body')
31
+ module LeanMail
32
+ # RFC 2821 message data representation.
33
+ class Data
34
+ attr_reader :from, :to, :subject, :message
35
+
36
+ def initialize(from, to, subject, message)
37
+ @from = from
38
+ @to = to
39
+ @subject = subject
40
+ @message = message
41
+ end
42
+
43
+ def to_s
44
+ <<~MAIL
45
+ From: #{@from}
46
+ To: #{to.instance_of?(Array) ? to.join(', ') : to}
47
+ Subject: #{subject}
48
+ Date: #{Time.new.rfc2822}
49
+ Message-Id: <#{SecureRandom.uuid}@#{Socket.gethostname}>
50
+
51
+ #{@message}
52
+ MAIL
53
+ end
54
+ end
55
+
56
+ # Email deliverer.
57
+ class Deliver
58
+ attr_reader :data
59
+
60
+ def initialize
61
+ @user = ENV.fetch('SMTP_USER', nil)
62
+ @password = ENV.fetch('SMTP_PASSWORD', nil)
63
+ @from = ENV.fetch('SMTP_FROM')
64
+
65
+ @host = ENV.fetch('SMTP_HOST')
66
+ @port = ENV.fetch('SMTP_PORT', 25)
67
+ @security = ENV.fetch('SMTP_SECURITY', nil)
68
+
69
+ initialize_smtp
70
+ end
71
+
72
+ # Send email.
73
+ #
74
+ # @param to [Array, String] In the format `Name <user@mail>` or `user@mail`.
75
+ # @param subject [String]
76
+ # @param body [String]
77
+ # @return [Deliver]
78
+ def call(to, subject, body)
79
+ @data = Data.new(@from, to, subject, body)
80
+
81
+ @smtp.start(Socket.gethostname, @user, @password, :plain) do |smtp|
82
+ smtp.send_message(@data.to_s, extract_addr(@from), extract_addrs(to))
83
+ end
84
+
85
+ self
86
+ end
87
+
88
+ protected
89
+
90
+ def initialize_smtp
91
+ @smtp = Net::SMTP.new(@host, @port)
92
+ maybe_enable_security(@security)
93
+ end
94
+
95
+ def maybe_enable_security(security)
96
+ case security
97
+ when 'tls' then @smtp.enable_tls
98
+ when 'starttls' then @smtp.enable_starttls
99
+ end
100
+ end
101
+
102
+ def extract_addr(str)
103
+ match = str.match(%r{<([^/]+)>})
104
+ return match[1] if match
105
+
106
+ str
107
+ end
108
+
109
+ def extract_addrs(to)
110
+ return to.map{ |addr| extract_addr(addr) } if to.instance_of?(Array)
111
+
112
+ extract_addr(to)
113
+ end
114
+ end
115
+
116
+ # Deliver email.
117
+ #
118
+ # @param to [Array, String] In the format `Name <user@mail>` or `user@mail`.
119
+ # @param subject [String]
120
+ # @param body [String]
121
+ # @return [Deliver]
122
+ def self.deliver(to, subject, body)
123
+ LeanMail::Deliver.new.call(to, subject, body)
124
+ end
125
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2022 Felix Freeman <libsys@hacktivista.org>
4
+ #
5
+ # This file is part of "LeanWeb" and licensed under the terms of the GNU Affero
6
+ # General Public License version 3 with permissions for compatibility with the
7
+ # Hacktivista General Public License. You should have received a copy of this
8
+ # license along with the software. If not, see <https://gnu.org/licenses/>.
9
+
10
+
11
+ module LeanWeb
12
+ module ControllerMixins
13
+ # {render_with_layout} for {Controller} with ERB, HAML, Tilt::EmacsOrg and
14
+ # Redcarpet Markdown support.
15
+ module RenderWithLayout
16
+ # Render a response with a layout.
17
+ #
18
+ # Depending on the `path` file contents and options `@content_for` might
19
+ # be filled. These contents must be inserted on the `head` or `body` of
20
+ # your layout file.
21
+ #
22
+ # All paths used on parameters can be relative to {VIEW_PATH} or absolute.
23
+ #
24
+ # @param path [String] Path to file to rendered.
25
+ # @param title [String] `@content_for[:title]` content.
26
+ # @param head [Hash] Things to be inserted on `@content_for[:head]`.
27
+ # @option head [String|Array] :js Javascript file path(s).
28
+ # @option head [String|Array] :jsm Javascript module file path (s).
29
+ # @option head [String|Array] :css CSS file path(s).
30
+ # @option head [String|Array] :raw Raw headers in HTML format.
31
+ # @param layout [String] Layout file path.
32
+ # @param sub_layout [String] Layout file to be inserted within layout.
33
+ # @param options [Hash] Options hash, to be given to the template engine
34
+ # based on file extension: `Tilt::EmacsOrg` for `.org` and `Redcarpet`
35
+ # for `.md`.
36
+ # @option options [String] :setupfile For `.org`.
37
+ # @option options [true] :toc For `.md`. To fill `@content_for[:toc]`
38
+ # render with option `:with_toc_data`.
39
+ # @yield On dynamic templates (such as Haml and ERB) you can yield.
40
+ def render_with_layout( # rubocop:disable Metrics/ParameterLists
41
+ path,
42
+ title: nil,
43
+ head: nil,
44
+ layout: 'layout.haml',
45
+ sub_layout: nil,
46
+ options: {},
47
+ &block
48
+ )
49
+ @content_for[:title] = title unless title.nil?
50
+ content = render_by_extension(path, sub_layout, options, &block)
51
+ prepare_head(head) unless head.nil?
52
+ render_response(layout, 'text/html'){ content }
53
+ end
54
+
55
+ protected
56
+
57
+ # @param path [String]
58
+ # @param layout [String]
59
+ # @param options [Hash] Check `Tilt::EmacsOrg` options.
60
+ # @option options [String] :setupfile can be absolute or relative to
61
+ # {VIEW_PATH}.
62
+ def render_org(path, layout: nil, options: {})
63
+ options[:setupfile] = "#{VIEW_PATH}/#{options[:setupfile]}" \
64
+ if options.include?(:setupfile) && options[:setupfile][0] != '/'
65
+
66
+ org_template = create_template(path, options)
67
+
68
+ @content_for[:title] = org_template.title \
69
+ unless @content_for.include?(:title)
70
+
71
+ if layout
72
+ create_template(layout).render(self){ org_template.render(self) }
73
+ else
74
+ org_template.render(self)
75
+ end
76
+ end
77
+
78
+ # @param path [String]
79
+ # @param layout [String]
80
+ # @param options [Hash] Check `RedCarpet` `render_options`.
81
+ # @option options [true] :toc To place an HTML based table of contents on
82
+ # `@content_for[:toc]` and render with option `:with_toc_data`.
83
+ def render_markdown(path, layout: nil, options: {})
84
+ maybe_render_markdown_toc!(path, options)
85
+ markdown_template = create_template(path, options)
86
+ if layout
87
+ create_template(layout).render(self){ markdown_template.render(self) }
88
+ else
89
+ markdown_template.render(self)
90
+ end
91
+ end
92
+
93
+ # If `options[:toc]` is set, deletes options `:toc` and adds
94
+ # `:with_toc_data`. Also adds `@content_for[:toc]`.
95
+ def maybe_render_markdown_toc!(path, options)
96
+ return unless options.include?(:toc)
97
+
98
+ require('redcarpet')
99
+ @content_for[:toc] = create_template(
100
+ path, { renderer: Redcarpet::Render::HTML_TOC }
101
+ ).render
102
+
103
+ options.delete(:toc)
104
+ options[:with_toc_data] = true
105
+ end
106
+
107
+ # @param path [String]
108
+ # @param layout [String]
109
+ # @param options [Hash] Check `Haml` options.
110
+ # @yield If block given.
111
+ def render_other(path, layout: nil, options: {})
112
+ template = create_template(path, options)
113
+ if layout
114
+ create_template(layout).render(self) do
115
+ template.render(self){ yield if block_given? }
116
+ end
117
+ else
118
+ template.render(self){ yield if block_given? }
119
+ end
120
+ end
121
+
122
+ def render_by_extension(path, layout, options, &block)
123
+ case File.extname(path)
124
+ when '.org'
125
+ render_org(path, layout: layout, options: options)
126
+ when '.md'
127
+ render_markdown(path, layout: layout, options: options)
128
+ else
129
+ render_other(path, layout: layout, options: options, &block)
130
+ end
131
+ end
132
+
133
+ def prepare_head(js: nil, jsm: nil, css: nil, raw: nil)
134
+ head = String.new
135
+ head << prepare_head_js(js) unless js.nil?
136
+ head << prepare_head_js(jsm, 'module') unless jsm.nil?
137
+ head << prepare_head_css(css) unless css.nil?
138
+ head << prepare_head_raw(raw) unless raw.nil?
139
+ @content_for[:head] = head
140
+ end
141
+
142
+ def prepare_head_js(js, type = 'application/javascript')
143
+ head = String.new
144
+ js = [js] if js.instance_of?(String)
145
+ js.each do |src|
146
+ head << "<script type='#{type}' src='#{src}'"
147
+ head << ' defer' if type != 'module'
148
+ head << "></script>\n"
149
+ end
150
+ head
151
+ end
152
+
153
+ def prepare_head_css(css)
154
+ head = String.new
155
+ css = [css] if css.instance_of?(String)
156
+ css.each do |src|
157
+ head << "<link rel='stylesheet' type='text/css' href='#{src}'>\n"
158
+ end
159
+ head
160
+ end
161
+
162
+ def prepare_head_raw(raw)
163
+ head = String.new
164
+ raw = [raw] if raw.instance_of?(String)
165
+ raw.each{ |src| head << "#{src}\n" }
166
+ head
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2022 Felix Freeman <libsys@hacktivista.org>
4
+ #
5
+ # This file is part of "LeanWeb" and licensed under the terms of the GNU Affero
6
+ # General Public License version 3 with permissions for compatibility with the
7
+ # Hacktivista General Public License. You should have received a copy of this
8
+ # license along with the software. If not, see <https://gnu.org/licenses/>.
9
+
10
+ require 'date'
11
+ require 'fileutils'
12
+ require 'json'
13
+ require 'rack/session/abstract/id'
14
+
15
+ module Rack
16
+ module Session
17
+ # A JSON File based session storage.
18
+ class JsonFile < Abstract::PersistedSecure
19
+ # Cleans session files that have not been used.
20
+ # @param to [Time|Date] Time from which file has not been accessed.
21
+ # Defaults to 00:00 hrs 1 month ago.
22
+ # @param dir [String] Directory from which to clean files. Defaults to
23
+ # `/tmp/rack.session`.
24
+ def self.clean(
25
+ to = Date.today.prev_month,
26
+ dir = "#{Dir.tmpdir}/rack.session"
27
+ )
28
+ to = to.to_time if to.instance_of?(Date)
29
+ Dir["#{dir}/*"].each do |file|
30
+ FileUtils.rm(file, force: true) if ::File.atime(file) < to
31
+ end
32
+ end
33
+
34
+ # @param options [Hash] Accepts a :session_dir path which defaults to
35
+ # `/tmp/rack.session`.
36
+ def initialize(app, options = {})
37
+ super(
38
+ app,
39
+ {
40
+ secure: ENV['RACK_ENV'] != 'development', # HTTPS unless in dev env
41
+ same_site: 'Strict' # don't allow cross-site sessions
42
+ }.merge!(options)
43
+ )
44
+ @session_dir = "#{Dir.tmpdir}/#{@key}" || @default_options[:session_dir]
45
+ end
46
+
47
+ # @return [Array] [self, Hash].
48
+ def find_session(_req, sid)
49
+ sid = generate_sid if sid.nil?
50
+
51
+ [sid, JSON.parse(::File.read("#{@session_dir}/#{sid}"))]
52
+ rescue Errno::ENOENT
53
+ [generate_sid, {}]
54
+ rescue JSON::ParserError
55
+ [sid, {}]
56
+ end
57
+
58
+ # @param sid [String] Only known ids are allowed, to avoid session
59
+ # fixation attacks.
60
+ # @return [self|false] session_id or false.
61
+ def write_session(_req, sid, session, _options)
62
+ return false unless ::File.exist?("#{@session_dir}/#{sid}")
63
+
64
+ ::File.open("#{@session_dir}/#{sid}", 'w', 0o600) do |f|
65
+ f << JSON.generate(session)
66
+ end
67
+ sid
68
+ end
69
+
70
+ # @return [self|nil] new session id or nil if options[:drop].
71
+ def delete_session(_req, sid, options)
72
+ FileUtils.rm("#{@session_dir}/#{sid}", force: true)
73
+ options&.include?(:drop) ? nil : generate_sid
74
+ end
75
+
76
+ def generate_sid(*)
77
+ sid = super
78
+ FileUtils.mkdir_p(@session_dir, mode: 0o700)
79
+ ::File.new("#{@session_dir}/#{sid}", 'w', 0o600).close
80
+ sid
81
+ end
82
+ end
83
+ end
84
+ end
data/lib/leanweb/app.rb CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  # Copyright 2022 Felix Freeman <libsys@hacktivista.org>
4
4
  #
5
- # This file is part of "LeanWeb" and licensed under the terms of the Hacktivista
6
- # General Public License version 0.1 or (at your option) any later version. You
7
- # should have received a copy of this license along with the software. If not,
8
- # see <https://hacktivista.org/licenses/>.
5
+ # This file is part of "LeanWeb" and licensed under the terms of the GNU Affero
6
+ # General Public License version 3 with permissions for compatibility with the
7
+ # Hacktivista General Public License. You should have received a copy of this
8
+ # license along with the software. If not, see <https://gnu.org/licenses/>.
9
9
 
10
10
  require 'rack'
11
11
 
@@ -20,19 +20,15 @@ module LeanWeb
20
20
  end
21
21
 
22
22
  # Entry point for dynamic routes (Rack).
23
+ #
23
24
  # @param env [Hash] `env` for Rack.
24
25
  def call(env)
25
26
  request = Rack::Request.new(env)
26
- route = @routes.find do |r|
27
- (r[:method] || 'GET') == request.request_method && begin
28
- r[:path] =~ request.path
29
- rescue TypeError
30
- r[:path] == request.path
31
- end
32
- end
33
- return [404, {}, ['Not found']] unless route_exists?(route)
27
+ route = find_route(request)
28
+ route = Route.new(**route) unless route.nil?
29
+ return [404, {}, ['Not found']] unless route&.available?
34
30
 
35
- Route.new(**route).respond(request)
31
+ route.respond(request)
36
32
  end
37
33
 
38
34
  # Build static routes to files.
@@ -42,31 +38,23 @@ module LeanWeb
42
38
  next unless route.static
43
39
 
44
40
  begin
45
- route.static.each { |seed| route.build(route.seed_path(seed)) }
41
+ route.static.each{ |seed| route.build(route.seed_path(seed)) }
46
42
  rescue NoMethodError
47
43
  route.build
48
44
  end
49
45
  end
50
46
  end
51
47
 
52
- # Initialize by evaluating the routes file.
53
- # Do this here so users don't freak out for using eval and rubocop is happy
54
- # on client side.
55
- # @param file [String] Routes file.
56
- def self.init(file = 'routes.rb')
57
- new(eval(File.read(file))) # rubocop:disable Security/Eval
58
- end
59
-
60
48
  protected
61
49
 
62
- # Check if route exists.
63
- # If not on development environment, serve only dynamic routes.
64
- # @param route [Hash]
65
- def route_exists?(route)
66
- return false if route.nil? \
67
- || (ENV['RACK_ENV'] != 'development' && route[:static] != false)
68
-
69
- true
50
+ def find_route(request)
51
+ @routes.find do |r|
52
+ (r[:method] || 'GET') == request.request_method && begin
53
+ r[:path] =~ request.path
54
+ rescue TypeError
55
+ r[:path] == request.path
56
+ end
57
+ end
70
58
  end
71
59
  end
72
60
  end