leanweb 0.1.3 → 0.3.0

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