ella 0.1.2
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 +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +56 -0
- data/LICENSE.txt +21 -0
- data/README.md +115 -0
- data/ella-0.1.0.gem +0 -0
- data/ella.gemspec +26 -0
- data/exe/ella +14 -0
- data/lib/ella.rb +45 -0
- data/lib/ella/cli.rb +114 -0
- data/lib/ella/controller.rb +36 -0
- data/lib/ella/generator.rb +42 -0
- data/lib/ella/generator/config_generator.rb +16 -0
- data/lib/ella/generator/controller_generator.rb +34 -0
- data/lib/ella/generator/destroyer.rb +44 -0
- data/lib/ella/generator/gemfile_generator.rb +15 -0
- data/lib/ella/generator/model_generator.rb +27 -0
- data/lib/ella/generator/project_generator.rb +88 -0
- data/lib/ella/generator/rackfile_generator.rb +76 -0
- data/lib/ella/generator/view_generator.rb +42 -0
- data/lib/ella/log.rb +70 -0
- data/lib/ella/model.rb +0 -0
- data/lib/ella/name_formatter.rb +32 -0
- data/lib/ella/pipeline.rb +99 -0
- data/lib/ella/reloader.rb +430 -0
- data/lib/ella/server.rb +107 -0
- data/lib/ella/template.rb +46 -0
- data/lib/ella/test.rb +9 -0
- data/lib/ella/version.rb +3 -0
- data/lib/ella/view.rb +0 -0
- data/templates/Gemfile +12 -0
- data/templates/configs/css.rb +20 -0
- data/templates/configs/js.rb +19 -0
- data/templates/configs/puma.rb +5 -0
- data/templates/controller +9 -0
- data/templates/controllers/root.rb +7 -0
- data/templates/main.rb +8 -0
- data/templates/model +6 -0
- data/templates/test +22 -0
- data/templates/views/layout.erb +18 -0
- data/templates/views/root/index.erb +3 -0
- data/version.txt +1 -0
- metadata +94 -0
data/lib/ella/model.rb
ADDED
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ella
|
|
4
|
+
# Ruby and rubyist convention demand that the project name be formatted in
|
|
5
|
+
# different ways depending on context. This class helps prevent repetition
|
|
6
|
+
# of name formatting.
|
|
7
|
+
# Thousands of Edabit exercises have prepared me for this, my finest hour.
|
|
8
|
+
# I am currently assuming that no one will be initializing in or using
|
|
9
|
+
# camelCase, as that format seems to have very little use in the Ruby
|
|
10
|
+
# community.
|
|
11
|
+
class NameFormatter
|
|
12
|
+
attr_reader :snake_case
|
|
13
|
+
|
|
14
|
+
def initialize(name)
|
|
15
|
+
Ella.abort('Project name must be a valid string.') if name.nil? || name.empty?
|
|
16
|
+
# If the project name is given in Pascal Case, save as snake case.
|
|
17
|
+
@snake_case = name =~ /^[A-Z]/ ? name.gsub(/([A-Z])/, '_\1')[1..-1].downcase : name
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def file_name
|
|
21
|
+
"#{@snake_case}.rb"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def pascal_case
|
|
25
|
+
@snake_case.split('_').map(&:capitalize).join('')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def human
|
|
29
|
+
@snake_case.split('_').map(&:capitalize).join(' ')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tempfile'
|
|
4
|
+
|
|
5
|
+
# Extra-special Message
|
|
6
|
+
# ---------------------
|
|
7
|
+
#
|
|
8
|
+
# I started making Ella because I found writing my own quick asset pipelines
|
|
9
|
+
# was much easier and faster and more bug-free than the "modern" and "correct"
|
|
10
|
+
# solutions. YAGNI should be a major cornerstorne of Ella.
|
|
11
|
+
#
|
|
12
|
+
# Rake may be considered iff this file ever becomes overly-complicated or slow,
|
|
13
|
+
# but I want to make sure that an easy pipeline is available by default. The
|
|
14
|
+
# user can set up another pipeline if they need something more complicated.
|
|
15
|
+
#
|
|
16
|
+
# -- kmc
|
|
17
|
+
|
|
18
|
+
module Ella
|
|
19
|
+
# Custom made super-simple assets pipeline. This KISS philosophy of this
|
|
20
|
+
# pipeline is:
|
|
21
|
+
#
|
|
22
|
+
# "data from files" --> "user defined filter in Ruby" --> "output file"
|
|
23
|
+
#
|
|
24
|
+
# Of course, Ella is modular, so the user should be able to disable it and
|
|
25
|
+
# set up their own assets pipeline of choice.
|
|
26
|
+
class Pipeline
|
|
27
|
+
def initialize(pipeline_type)
|
|
28
|
+
@type = pipeline_type
|
|
29
|
+
|
|
30
|
+
Log.info("Initializing #{@type.upcase} pipeline...")
|
|
31
|
+
set_io_directories
|
|
32
|
+
initialize_tempfile
|
|
33
|
+
load File.join(Dir.pwd, "configs/#{@type}.rb")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Because this is user-defined code, any exception is possible. Having
|
|
37
|
+
# to restart the development server every time there is some error in
|
|
38
|
+
# the filter is *NOT DESIRABLE*.
|
|
39
|
+
def listen
|
|
40
|
+
run # Public files are not persistent, so this must be run on startup.
|
|
41
|
+
@listener = Listen.to(@input_dir) do |modified, added, removed|
|
|
42
|
+
report_listen_results(modified, added, removed)
|
|
43
|
+
run
|
|
44
|
+
rescue => e
|
|
45
|
+
report_listen_error(e)
|
|
46
|
+
end
|
|
47
|
+
@listener.start
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def run
|
|
51
|
+
@tempfile.close
|
|
52
|
+
@tempfile.unlink
|
|
53
|
+
@tempfile = Tempfile.new(['', ".#{@type}"], @output_dir)
|
|
54
|
+
@tempfile.write(filter)
|
|
55
|
+
@tempfile.rewind
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def set_io_directories
|
|
61
|
+
@input_dir = File.join(Dir.pwd, "assets/#{@type}")
|
|
62
|
+
@output_dir = File.join(Dir.pwd, "public/#{@type}")
|
|
63
|
+
Dir.mkdir(@input_dir) unless Dir.exist?(@input_dir)
|
|
64
|
+
Dir.mkdir(@output_dir) unless Dir.exist?(@output_dir)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def initialize_tempfile
|
|
68
|
+
@tempfile = Tempfile.new(['', ".#{@type}"], @output_dir)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def report_listen_results(modified, added, removed)
|
|
72
|
+
Log.info("#{@type.capitalize} Pipeline detected change:")
|
|
73
|
+
Log.info("Modified: #{modified}") if modified
|
|
74
|
+
Log.info("Modified: #{added}") if added
|
|
75
|
+
Log.info("Modified: #{removed}") if removed
|
|
76
|
+
Log.info("#{@type.capitalize} Pipeline recompiling")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def report_listen_error(e)
|
|
80
|
+
Log.error('Error in listener:')
|
|
81
|
+
puts e.backtrace
|
|
82
|
+
puts e.inspect
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def filter
|
|
86
|
+
# TODO: Something more dynamic should go here. People should be able to
|
|
87
|
+
# create their own pipelines.
|
|
88
|
+
if @type == 'css'
|
|
89
|
+
css
|
|
90
|
+
elsif @type == 'js'
|
|
91
|
+
js
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def asset_data(*files)
|
|
96
|
+
files.inject('') { |str, fname| str += File.read(File.join(@input_dir, fname)) }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
require 'sinatra/base'
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2007, 2008, 2009 Blake Mizerany
|
|
4
|
+
# Copyright (c) 2010-2017 Konstantin Haase
|
|
5
|
+
# Copyright (c) 2015-2017 Zachary Scott
|
|
6
|
+
|
|
7
|
+
# This is a so-far-untouched fork of the listener in 'sinatra-contrib', and is
|
|
8
|
+
# destributed under the MIT license.
|
|
9
|
+
# For some reason some combination of updates messed up the paths and made
|
|
10
|
+
# the version from the "sinatra-contrib" fail to make it to the Ruby load path.
|
|
11
|
+
# It did not seem like a good idea for one gem to mess with the load path to
|
|
12
|
+
# accommodate another gem.
|
|
13
|
+
# Either this will be maintained as part of Ella, or Ella will switch back to
|
|
14
|
+
# sinatra-contrib whenever the mysterious issue is resolved. The future's not
|
|
15
|
+
# ours to see.
|
|
16
|
+
module Sinatra
|
|
17
|
+
|
|
18
|
+
# = Sinatra::Reloader
|
|
19
|
+
#
|
|
20
|
+
# Extension to reload modified files. Useful during development,
|
|
21
|
+
# since it will automatically require files defining routes, filters,
|
|
22
|
+
# error handlers and inline templates, with every incoming request,
|
|
23
|
+
# but only if they have been updated.
|
|
24
|
+
#
|
|
25
|
+
# == Usage
|
|
26
|
+
#
|
|
27
|
+
# === Classic Application
|
|
28
|
+
#
|
|
29
|
+
# To enable the reloader in a classic application all you need to do is
|
|
30
|
+
# require it:
|
|
31
|
+
#
|
|
32
|
+
# require "sinatra"
|
|
33
|
+
# require "sinatra/reloader" if development?
|
|
34
|
+
#
|
|
35
|
+
# # Your classic application code goes here...
|
|
36
|
+
#
|
|
37
|
+
# === Modular Application
|
|
38
|
+
#
|
|
39
|
+
# To enable the reloader in a modular application all you need to do is
|
|
40
|
+
# require it, and then, register it:
|
|
41
|
+
#
|
|
42
|
+
# require "sinatra/base"
|
|
43
|
+
# require "sinatra/reloader"
|
|
44
|
+
#
|
|
45
|
+
# class MyApp < Sinatra::Base
|
|
46
|
+
# configure :development do
|
|
47
|
+
# register Sinatra::Reloader
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# # Your modular application code goes here...
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# == Using the Reloader in Other Environments
|
|
54
|
+
#
|
|
55
|
+
# By default, the reloader is only enabled for the development
|
|
56
|
+
# environment. Similar to registering the reloader in a modular
|
|
57
|
+
# application, a classic application requires manually enabling the
|
|
58
|
+
# extension for it to be available in a non-development environment.
|
|
59
|
+
#
|
|
60
|
+
# require "sinatra"
|
|
61
|
+
# require "sinatra/reloader"
|
|
62
|
+
#
|
|
63
|
+
# configure :production do
|
|
64
|
+
# enable :reloader
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# == Changing the Reloading Policy
|
|
68
|
+
#
|
|
69
|
+
# You can refine the reloading policy with +also_reload+ and
|
|
70
|
+
# +dont_reload+, to customize which files should, and should not, be
|
|
71
|
+
# reloaded, respectively. You can also use +after_reload+ to execute a
|
|
72
|
+
# block after any file being reloaded.
|
|
73
|
+
#
|
|
74
|
+
# === Classic Application
|
|
75
|
+
#
|
|
76
|
+
# Simply call the methods:
|
|
77
|
+
#
|
|
78
|
+
# require "sinatra"
|
|
79
|
+
# require "sinatra/reloader" if development?
|
|
80
|
+
#
|
|
81
|
+
# also_reload '/path/to/some/file'
|
|
82
|
+
# dont_reload '/path/to/other/file'
|
|
83
|
+
# after_reload do
|
|
84
|
+
# puts 'reloaded'
|
|
85
|
+
# end
|
|
86
|
+
#
|
|
87
|
+
# # Your classic application code goes here...
|
|
88
|
+
#
|
|
89
|
+
# === Modular Application
|
|
90
|
+
#
|
|
91
|
+
# Call the methods inside the +configure+ block:
|
|
92
|
+
#
|
|
93
|
+
# require "sinatra/base"
|
|
94
|
+
# require "sinatra/reloader"
|
|
95
|
+
#
|
|
96
|
+
# class MyApp < Sinatra::Base
|
|
97
|
+
# configure :development do
|
|
98
|
+
# register Sinatra::Reloader
|
|
99
|
+
# also_reload '/path/to/some/file'
|
|
100
|
+
# dont_reload '/path/to/other/file'
|
|
101
|
+
# after_reload do
|
|
102
|
+
# puts 'reloaded'
|
|
103
|
+
# end
|
|
104
|
+
# end
|
|
105
|
+
#
|
|
106
|
+
# # Your modular application code goes here...
|
|
107
|
+
# end
|
|
108
|
+
#
|
|
109
|
+
module Reloader
|
|
110
|
+
|
|
111
|
+
# Watches a file so it can tell when it has been updated, and what
|
|
112
|
+
# elements does it contain.
|
|
113
|
+
class Watcher
|
|
114
|
+
|
|
115
|
+
# Represents an element of a Sinatra application that may need to
|
|
116
|
+
# be reloaded. An element could be:
|
|
117
|
+
# * a route
|
|
118
|
+
# * a filter
|
|
119
|
+
# * an error handler
|
|
120
|
+
# * a middleware
|
|
121
|
+
# * inline templates
|
|
122
|
+
#
|
|
123
|
+
# Its +representation+ attribute is there to allow to identify the
|
|
124
|
+
# element within an application, that is, to match it with its
|
|
125
|
+
# Sinatra's internal representation.
|
|
126
|
+
class Element < Struct.new(:type, :representation)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Collection of file +Watcher+ that can be associated with a
|
|
130
|
+
# Sinatra application. That way, we can know which files belong
|
|
131
|
+
# to a given application and which files have been modified. It
|
|
132
|
+
# also provides a mechanism to inform a Watcher of the elements
|
|
133
|
+
# defined in the file being watched and if its changes should be
|
|
134
|
+
# ignored.
|
|
135
|
+
class List
|
|
136
|
+
@app_list_map = Hash.new { |hash, key| hash[key] = new }
|
|
137
|
+
|
|
138
|
+
# Returns the +List+ for the application +app+.
|
|
139
|
+
def self.for(app)
|
|
140
|
+
@app_list_map[app]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Creates a new +List+ instance.
|
|
144
|
+
def initialize
|
|
145
|
+
@path_watcher_map = Hash.new do |hash, key|
|
|
146
|
+
hash[key] = Watcher.new(key)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Lets the +Watcher+ for the file located at +path+ know that the
|
|
151
|
+
# +element+ is defined there, and adds the +Watcher+ to the +List+,
|
|
152
|
+
# if it isn't already there.
|
|
153
|
+
def watch(path, element)
|
|
154
|
+
watcher_for(path).elements << element
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Tells the +Watcher+ for the file located at +path+ to ignore
|
|
158
|
+
# the file changes, and adds the +Watcher+ to the +List+, if
|
|
159
|
+
# it isn't already there.
|
|
160
|
+
def ignore(path)
|
|
161
|
+
watcher_for(path).ignore
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Adds a +Watcher+ for the file located at +path+ to the
|
|
165
|
+
# +List+, if it isn't already there.
|
|
166
|
+
def watcher_for(path)
|
|
167
|
+
@path_watcher_map[File.expand_path(path)]
|
|
168
|
+
end
|
|
169
|
+
alias watch_file watcher_for
|
|
170
|
+
|
|
171
|
+
# Returns an array with all the watchers in the +List+.
|
|
172
|
+
def watchers
|
|
173
|
+
@path_watcher_map.values
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Returns an array with all the watchers in the +List+ that
|
|
177
|
+
# have been updated.
|
|
178
|
+
def updated
|
|
179
|
+
watchers.find_all(&:updated?)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
attr_reader :path, :elements, :mtime
|
|
184
|
+
|
|
185
|
+
# Creates a new +Watcher+ instance for the file located at +path+.
|
|
186
|
+
def initialize(path)
|
|
187
|
+
@ignore = nil
|
|
188
|
+
@path, @elements = path, []
|
|
189
|
+
update
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Indicates whether or not the file being watched has been modified.
|
|
193
|
+
def updated?
|
|
194
|
+
!ignore? && !removed? && mtime != File.mtime(path)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Updates the mtime of the file being watched.
|
|
198
|
+
def update
|
|
199
|
+
@mtime = File.mtime(path)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Indicates whether or not the file being watched has inline
|
|
203
|
+
# templates.
|
|
204
|
+
def inline_templates?
|
|
205
|
+
elements.any? { |element| element.type == :inline_templates }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Informs that the modifications to the file being watched
|
|
209
|
+
# should be ignored.
|
|
210
|
+
def ignore
|
|
211
|
+
@ignore = true
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Indicates whether or not the modifications to the file being
|
|
215
|
+
# watched should be ignored.
|
|
216
|
+
def ignore?
|
|
217
|
+
!!@ignore
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Indicates whether or not the file being watched has been removed.
|
|
221
|
+
def removed?
|
|
222
|
+
!File.exist?(path)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
MUTEX_FOR_PERFORM = Mutex.new
|
|
227
|
+
|
|
228
|
+
# Allow a block to be executed after any file being reloaded
|
|
229
|
+
@@after_reload = []
|
|
230
|
+
def after_reload(&block)
|
|
231
|
+
@@after_reload << block
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# When the extension is registered it extends the Sinatra application
|
|
235
|
+
# +klass+ with the modules +BaseMethods+ and +ExtensionMethods+ and
|
|
236
|
+
# defines a before filter to +perform+ the reload of the modified files.
|
|
237
|
+
def self.registered(klass)
|
|
238
|
+
@reloader_loaded_in ||= {}
|
|
239
|
+
return if @reloader_loaded_in[klass]
|
|
240
|
+
|
|
241
|
+
@reloader_loaded_in[klass] = true
|
|
242
|
+
|
|
243
|
+
klass.extend BaseMethods
|
|
244
|
+
klass.extend ExtensionMethods
|
|
245
|
+
klass.set(:reloader) { klass.development? }
|
|
246
|
+
klass.set(:reload_templates) { klass.reloader? }
|
|
247
|
+
klass.before do
|
|
248
|
+
if klass.reloader?
|
|
249
|
+
MUTEX_FOR_PERFORM.synchronize { Reloader.perform(klass) }
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
klass.set(:inline_templates, klass.app_file) if klass == Sinatra::Application
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Reloads the modified files, adding, updating and removing the
|
|
256
|
+
# needed elements.
|
|
257
|
+
def self.perform(klass)
|
|
258
|
+
Watcher::List.for(klass).updated.each do |watcher|
|
|
259
|
+
klass.set(:inline_templates, watcher.path) if watcher.inline_templates?
|
|
260
|
+
watcher.elements.each { |element| klass.deactivate(element) }
|
|
261
|
+
$LOADED_FEATURES.delete(watcher.path)
|
|
262
|
+
require watcher.path
|
|
263
|
+
watcher.update
|
|
264
|
+
end
|
|
265
|
+
@@after_reload.each(&:call)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Contains the methods defined in Sinatra::Base that are overridden.
|
|
269
|
+
module BaseMethods
|
|
270
|
+
# Protects Sinatra::Base.run! from being called more than once.
|
|
271
|
+
def run!(*args)
|
|
272
|
+
if settings.reloader?
|
|
273
|
+
super unless running?
|
|
274
|
+
else
|
|
275
|
+
super
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Does everything Sinatra::Base#route does, but it also tells the
|
|
280
|
+
# +Watcher::List+ for the Sinatra application to watch the defined
|
|
281
|
+
# route.
|
|
282
|
+
#
|
|
283
|
+
# Note: We are using #compile! so we don't interfere with extensions
|
|
284
|
+
# changing #route.
|
|
285
|
+
def compile!(verb, path, block, **options)
|
|
286
|
+
source_location = block.respond_to?(:source_location) ?
|
|
287
|
+
block.source_location.first : caller_files[1]
|
|
288
|
+
signature = super
|
|
289
|
+
watch_element(
|
|
290
|
+
source_location, :route, { :verb => verb, :signature => signature }
|
|
291
|
+
)
|
|
292
|
+
signature
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Does everything Sinatra::Base#inline_templates= does, but it also
|
|
296
|
+
# tells the +Watcher::List+ for the Sinatra application to watch the
|
|
297
|
+
# inline templates in +file+ or the file who made the call to this
|
|
298
|
+
# method.
|
|
299
|
+
def inline_templates=(file=nil)
|
|
300
|
+
file = (file.nil? || file == true) ?
|
|
301
|
+
(caller_files[1] || File.expand_path($0)) : file
|
|
302
|
+
watch_element(file, :inline_templates)
|
|
303
|
+
super
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Does everything Sinatra::Base#use does, but it also tells the
|
|
307
|
+
# +Watcher::List+ for the Sinatra application to watch the middleware
|
|
308
|
+
# being used.
|
|
309
|
+
def use(middleware, *args, &block)
|
|
310
|
+
path = caller_files[1] || File.expand_path($0)
|
|
311
|
+
watch_element(path, :middleware, [middleware, args, block])
|
|
312
|
+
super
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Does everything Sinatra::Base#add_filter does, but it also tells
|
|
316
|
+
# the +Watcher::List+ for the Sinatra application to watch the defined
|
|
317
|
+
# filter.
|
|
318
|
+
def add_filter(type, path = nil, **options, &block)
|
|
319
|
+
source_location = block.respond_to?(:source_location) ?
|
|
320
|
+
block.source_location.first : caller_files[1]
|
|
321
|
+
result = super
|
|
322
|
+
watch_element(source_location, :"#{type}_filter", filters[type].last)
|
|
323
|
+
result
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Does everything Sinatra::Base#error does, but it also tells the
|
|
327
|
+
# +Watcher::List+ for the Sinatra application to watch the defined
|
|
328
|
+
# error handler.
|
|
329
|
+
def error(*codes, &block)
|
|
330
|
+
path = caller_files[1] || File.expand_path($0)
|
|
331
|
+
result = super
|
|
332
|
+
codes.each do |c|
|
|
333
|
+
watch_element(path, :error, :code => c, :handler => @errors[c])
|
|
334
|
+
end
|
|
335
|
+
result
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Does everything Sinatra::Base#register does, but it also lets the
|
|
339
|
+
# reloader know that an extension is being registered, because the
|
|
340
|
+
# elements defined in its +registered+ method need a special treatment.
|
|
341
|
+
def register(*extensions, &block)
|
|
342
|
+
start_registering_extension
|
|
343
|
+
result = super
|
|
344
|
+
stop_registering_extension
|
|
345
|
+
result
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Does everything Sinatra::Base#register does and then registers the
|
|
349
|
+
# reloader in the +subclass+.
|
|
350
|
+
def inherited(subclass)
|
|
351
|
+
result = super
|
|
352
|
+
subclass.register Sinatra::Reloader
|
|
353
|
+
result
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Contains the methods that the extension adds to the Sinatra application.
|
|
358
|
+
module ExtensionMethods
|
|
359
|
+
# Removes the +element+ from the Sinatra application.
|
|
360
|
+
def deactivate(element)
|
|
361
|
+
case element.type
|
|
362
|
+
when :route then
|
|
363
|
+
verb = element.representation[:verb]
|
|
364
|
+
signature = element.representation[:signature]
|
|
365
|
+
(routes[verb] ||= []).delete(signature)
|
|
366
|
+
when :middleware then
|
|
367
|
+
@middleware.delete(element.representation)
|
|
368
|
+
when :before_filter then
|
|
369
|
+
filters[:before].delete(element.representation)
|
|
370
|
+
when :after_filter then
|
|
371
|
+
filters[:after].delete(element.representation)
|
|
372
|
+
when :error then
|
|
373
|
+
code = element.representation[:code]
|
|
374
|
+
handler = element.representation[:handler]
|
|
375
|
+
@errors.delete(code) if @errors[code] == handler
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Indicates with a +glob+ which files should be reloaded if they
|
|
380
|
+
# have been modified. It can be called several times.
|
|
381
|
+
def also_reload(*glob)
|
|
382
|
+
Dir[*glob].each { |path| Watcher::List.for(self).watch_file(path) }
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Indicates with a +glob+ which files should not be reloaded even if
|
|
386
|
+
# they have been modified. It can be called several times.
|
|
387
|
+
def dont_reload(*glob)
|
|
388
|
+
Dir[*glob].each { |path| Watcher::List.for(self).ignore(path) }
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
private
|
|
392
|
+
|
|
393
|
+
# attr_reader :register_path warn on -w (private attribute)
|
|
394
|
+
def register_path; @register_path ||= nil; end
|
|
395
|
+
|
|
396
|
+
# Indicates an extesion is being registered.
|
|
397
|
+
def start_registering_extension
|
|
398
|
+
@register_path = caller_files[2]
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Indicates the extesion has already been registered.
|
|
402
|
+
def stop_registering_extension
|
|
403
|
+
@register_path = nil
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# Indicates whether or not an extension is being registered.
|
|
407
|
+
def registering_extension?
|
|
408
|
+
!register_path.nil?
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# Builds a Watcher::Element from +type+ and +representation+ and
|
|
412
|
+
# tells the Watcher::List for the current application to watch it
|
|
413
|
+
# in the file located at +path+.
|
|
414
|
+
#
|
|
415
|
+
# If an extension is being registered, it also tells the list to
|
|
416
|
+
# watch it in the file where the extension has been registered.
|
|
417
|
+
# This prevents the duplication of the elements added by the
|
|
418
|
+
# extension in its +registered+ method with every reload.
|
|
419
|
+
def watch_element(path, type, representation=nil)
|
|
420
|
+
list = Watcher::List.for(self)
|
|
421
|
+
element = Watcher::Element.new(type, representation)
|
|
422
|
+
list.watch(path, element)
|
|
423
|
+
list.watch(register_path, element) if registering_extension?
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
register Reloader
|
|
429
|
+
Delegator.delegate :also_reload, :dont_reload
|
|
430
|
+
end
|