Class: Epuber::Server
- Inherits:
-
Sinatra::Base
- Object
- Sinatra::Base
- Epuber::Server
- Defined in:
- lib/epuber/server.rb,
lib/epuber/server/handlers.rb
Overview
API:
- LATER
-
/file/<path-or-pattern> – displays pretty file (image, text file) (for example: /file/text/s01.xhtml or
/file/text/s01.bade)
Defined Under Namespace
Classes: ShowExceptions
Helpers collapse
- ._compile_book ⇒ Object
- .changes_detected(modified, added, removed) ⇒ Object
- .compile_book(&completion) ⇒ Object
- .filter_not_project_files(files_paths) ⇒ Array<String>
- .notify_clients(type, data = nil) ⇒ Object
- .relative_path_to_book_file(path) ⇒ String
- .reload_bookspec ⇒ Object
- .send_to_clients(message) ⇒ Object
- #add_auto_refresh_script(html_doc) ⇒ Object
- #add_file_to_head(type, html_doc, file_path) ⇒ Object
- #add_keyboard_control_script(html_doc, previous_path, next_path) ⇒ Object
- #add_meta_to_head(name, content, html_doc, force: false) ⇒ Object
- #add_script_file_to_head(html_doc, file_name, *args) ⇒ Object
- #add_script_to_head(html_doc, script_text) ⇒ Object
-
#find_file(pattern = params[:splat].first, source_path: build_path) ⇒ String
Path to file.
- #handle_file(file_path) ⇒ Object
- #handle_websocket(path) ⇒ Object
- #spine_file_at(index) ⇒ Epuber::Book::File?
Class Method Summary collapse
-
._log(level, message) ⇒ Object
Nil.
-
.build_path ⇒ String
Base path.
- .instance_class_accessor(name) ⇒ Object
- .render_bade(file_path) ⇒ String
-
.run!(book, target, verbose: false) ⇒ Object
Nil.
- .sockets ⇒ Object
- .start_listening_if_needed ⇒ Object
- .verbose=(verbose) ⇒ Object
Instance Method Summary collapse
- #_log(level, message) ⇒ Object
- #book ⇒ Epuber::Book::Book
-
#build_path ⇒ String
Base path.
- #file_resolver ⇒ Epuber::Compiler::FileResolver
- #handle_bade(file_path) ⇒ (Fixnum, String)
- #handle_server_bade(file_name) ⇒ (Fixnum, String)
- #handle_xhtml_file(file_path) ⇒ (Fixnum, String)
-
#initialize ⇒ Server
constructor
A new instance of Server.
- #listener ⇒ Listener
- #sockets ⇒ Array<SinatraWebsocket::Connection>
- #spine ⇒ Array<Epuber::Compiler::File>
- #target ⇒ Epuber::Book::Target
- #verbose ⇒ FalseClass, TrueClass
Constructor Details
#initialize ⇒ Server
Returns a new instance of Server.
145 146 147 148 149 150 |
# File 'lib/epuber/server.rb', line 145 def initialize super _log :ui, 'Init compile' self.class.compile_book end |
Class Method Details
._compile_book ⇒ Object
341 342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/epuber/server.rb', line 341 def self._compile_book compiler = Epuber::Compiler.new(book, target) compiler.compile(build_path) self.file_resolver = compiler.file_resolver true rescue StandardError => e self.file_resolver = compiler.file_resolver Epuber::UI.error("Compile error: #{e}", location: e) false end |
._log(level, message) ⇒ Object
Returns nil.
187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/epuber/server.rb', line 187 def self._log(level, ) case level when :ui UI.info when :info UI.info "INFO: #{}" if verbose when :get UI.info " GET: #{}" if verbose when :ws UI.info " WS: #{}" if verbose else raise "Unknown log level #{level}" end end |
.build_path ⇒ String
Returns base path.
172 173 174 |
# File 'lib/epuber/server.rb', line 172 def self.build_path Epuber::Config.instance.build_path(target) end |
.changes_detected(modified, added, removed) ⇒ Object
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/epuber/server.rb', line 408 def self.changes_detected(modified, added, removed) all_changed = (modified + added + removed).uniq.map(&:unicode_normalize) reload_bookspec if all_changed.any? { |file| file == book.file_path } changed = filter_not_project_files(all_changed) || [] return if changed.count.zero? notify_clients(:compile_start) _log :ui, "#{Time.now} Compiling" compile_book do |success| unless success _log :ui, 'Skipping other steps' notify_clients(:compile_end) next end _log :ui, 'Notifying clients' # transform all paths to relatives to the server changed.map! do |file| relative = relative_path_to_book_file(file) File.join('', 'book', relative) unless relative.nil? end # remove nil paths (for example bookspec can't be found so the relative path is nil) changed.compact! changed_only_styles = changed.all? do |file| file.end_with?(*Epuber::Compiler::FileFinders::GROUP_EXTENSIONS[:style]) end if changed.size.positive? && changed_only_styles notify_clients(:styles, changed) else notify_clients(:reload, changed) end end end |
.compile_book(&completion) ⇒ Object
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/epuber/server.rb', line 355 def self.compile_book(&completion) if !@compilation_thread.nil? && @compilation_thread.status != false @compilation_thread.kill @compilation_thread = nil end @compilation_thread = Thread.new do result = _compile_book completion&.call(result) end return unless completion.nil? @compilation_thread.join end |
.filter_not_project_files(files_paths) ⇒ Array<String>
398 399 400 401 402 |
# File 'lib/epuber/server.rb', line 398 def self.filter_not_project_files(files_paths) return nil if file_resolver.nil? files_paths.select { |file| file_resolver.file_with_source_path(file) || book.file_path == file } end |
.instance_class_accessor(name) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/epuber/server.rb', line 53 def self.instance_class_accessor(name) instance_name = "@#{name}" define_singleton_method(name) do instance_variable_get(instance_name) end define_singleton_method("#{name}=") do |new_value| instance_variable_set(instance_name, new_value) end define_method(name) do self.class.send(name) end define_method("#{name}=") do |new_value| self.class.send("#{name}=", new_value) end end |
.notify_clients(type, data = nil) ⇒ Object
382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/epuber/server.rb', line 382 def self.notify_clients(type, data = nil) _log :info, "Notifying clients with type #{type.inspect}" raise "Not known type `#{type}`" unless %i[styles reload compile_start compile_end].include?(type) = { name: type, } [:data] = data unless data.nil? send_to_clients(.to_json) end |
.relative_path_to_book_file(path) ⇒ String
233 234 235 236 237 238 |
# File 'lib/epuber/server.rb', line 233 def self.relative_path_to_book_file(path) file = file_resolver.file_with_source_path(path) return if file.nil? file.pkg_destination_path end |
.reload_bookspec ⇒ Object
328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/epuber/server.rb', line 328 def self.reload_bookspec last_target = target Config.instance.bookspec = nil self.book = Config.instance.bookspec if (new_target = book.target_named(last_target.name)) self.target = new_target else self.target = book.targets.first _log :ui, "[!] Not found previous target after reloading bookspec file, jumping to first #{target.name}" end end |
.render_bade(file_path) ⇒ String
61 62 63 64 65 66 |
# File 'lib/epuber/server/handlers.rb', line 61 def self.render_bade(file_path) renderer = Bade::Renderer.from_file(file_path) .with_locals(book: book, target: target, file_resolver: file_resolver) renderer.render(new_line: '', indent: '') end |
.run!(book, target, verbose: false) ⇒ Object
Returns nil.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/epuber/server.rb', line 105 def self.run!(book, target, verbose: false) Encoding.default_internal = Encoding::UTF_8 if Encoding.default_internal.nil? self.book = book self.target = target self.verbose = verbose start_listening_if_needed old_stderr = $stderr $stderr = StringIO.new unless verbose super() do |server| $stderr = old_stderr UI.info "Started development server on #{server.host}:#{server.port}" host = if server.host == '0.0.0.0' 'localhost' else server.host end yield URI("http://#{host}:#{server.port}") if block_given? end end |
.send_to_clients(message) ⇒ Object
373 374 375 376 377 378 379 |
# File 'lib/epuber/server.rb', line 373 def self.send_to_clients() _log :info, "sending message to clients #{.inspect}" sockets.each do |ws| ws.send() end end |
.sockets ⇒ Object
95 96 97 |
# File 'lib/epuber/server.rb', line 95 def self.sockets @sockets ||= [] end |
.start_listening_if_needed ⇒ Object
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/epuber/server.rb', line 152 def self.start_listening_if_needed return unless listener.nil? self.listener = Listen.to(Config.instance.project_path, debug: true) do |modified, added, removed| changes_detected(modified, added, removed) rescue StandardError => e # print error, do not send error further, listener will die otherwise warn e warn e.backtrace end listener.ignore(/\.idea/) listener.ignore(/#{Config.instance.working_path}/) listener.ignore(%r{#{Config::WORKING_PATH}/}) listener.start end |
.verbose=(verbose) ⇒ Object
131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/epuber/server.rb', line 131 def self.verbose=(verbose) @verbose = verbose @default_thin_logger ||= Thin::Logging.logger if verbose Thin::Logging.logger = @default_thin_logger else Thin::Logging.logger = ::Logger.new(nil) Thin::Logging.logger.level = :fatal end set :logging, verbose end |
Instance Method Details
#_log(level, message) ⇒ Object
202 203 204 |
# File 'lib/epuber/server.rb', line 202 def _log(level, ) self.class._log(level, ) end |
#add_auto_refresh_script(html_doc) ⇒ Object
277 278 279 280 281 282 283 |
# File 'lib/epuber/server.rb', line 277 def add_auto_refresh_script(html_doc) add_file_to_head(:js, html_doc, 'auto_refresh/reloader.coffee') add_file_to_head(:js, html_doc, 'auto_refresh/connector.coffee') add_file_to_head(:js, html_doc, 'auto_refresh/protocol.coffee') add_file_to_head(:js, html_doc, 'auto_refresh/auto_refresh.coffee') add_script_to_head(html_doc, 'var auto_refresh = new AutoRefresh(window, console);') end |
#add_file_to_head(type, html_doc, file_path) ⇒ Object
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/epuber/server.rb', line 295 def add_file_to_head(type, html_doc, file_path) head = html_doc.at_css('head') node = case type when :style html_doc.create_element('link', href: "/server/raw/#{file_path}", rel: 'stylesheet', type: 'text/css') when :js html_doc.create_element('script', src: "/server/raw/#{file_path}", type: 'text/javascript') else raise "Unknown file type `#{type}`" end return if head.css('script, link').any? do |n| (!n['href'].nil? && n['href'] == node['href']) || (!n['src'].nil? && n['src'] == node['src']) end head.add_child(node) end |
#add_keyboard_control_script(html_doc, previous_path, next_path) ⇒ Object
287 288 289 290 291 |
# File 'lib/epuber/server.rb', line 287 def add_keyboard_control_script(html_doc, previous_path, next_path) add_script_file_to_head(html_doc, 'keyboard_control.coffee', '$previous_path' => previous_path, '$next_path' => next_path) end |
#add_meta_to_head(name, content, html_doc, force: false) ⇒ Object
317 318 319 320 321 322 323 324 325 326 |
# File 'lib/epuber/server.rb', line 317 def (name, content, html_doc, force: false) head = html_doc.at_css('head') = head.at_css("meta[name=\"#{name}\"]") return if force == false && !.nil? ||= html_doc.create_element('meta') ['name'] = name ['content'] = content head.add_child() end |
#add_script_file_to_head(html_doc, file_name, *args) ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/epuber/server.rb', line 240 def add_script_file_to_head(html_doc, file_name, *args) source = File.read(File.("server/#{file_name}", File.dirname(__FILE__))) source = CoffeeScript.compile(source) if File.extname(file_name) == '.coffee' args.each do |hash| hash.each do |key, value| opt_value = if value "'#{value}'" else 'null' end source.gsub!(key, opt_value) end end source = yield source if block_given? add_script_to_head(html_doc, source) end |
#add_script_to_head(html_doc, script_text) ⇒ Object
263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/epuber/server.rb', line 263 def add_script_to_head(html_doc, script_text) script_node = html_doc.create_element('script', script_text, type: 'text/javascript') head = html_doc.at_css('head') if head.nil? head = html_doc.create_element('head') html_doc.at_css('html').add_child(head) end head.add_child(script_node) end |
#book ⇒ Epuber::Book::Book
73 |
# File 'lib/epuber/server.rb', line 73 instance_class_accessor :book |
#build_path ⇒ String
Returns base path.
178 179 180 |
# File 'lib/epuber/server.rb', line 178 def build_path self.class.build_path end |
#file_resolver ⇒ Epuber::Compiler::FileResolver
85 |
# File 'lib/epuber/server.rb', line 85 instance_class_accessor :file_resolver |
#find_file(pattern = params[:splat].first, source_path: build_path) ⇒ String
Returns path to file.
215 216 217 218 |
# File 'lib/epuber/server.rb', line 215 def find_file(pattern = params[:splat].first, source_path: build_path) finder = Compiler::FileFinders::Normal.new(source_path) finder.find_files(pattern).first end |
#handle_bade(file_path) ⇒ (Fixnum, String)
50 51 52 53 54 55 |
# File 'lib/epuber/server/handlers.rb', line 50 def handle_bade(file_path) [200, self.class.render_bade(file_path)] rescue StandardError => e env['sinatra.error'] = e ShowExceptions.new(self).call(env) end |
#handle_file(file_path) ⇒ Object
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 |
# File 'lib/epuber/server.rb', line 486 def handle_file(file_path) return not_found unless File.exist?(file_path) mtime = File.mtime(file_path) last_modified(mtime) etag(mtime.to_s) cache_control :public, :must_revalidate case File.extname(file_path) when '.styl' content_type('text/css') body(Stylus.compile(::File.new(file_path))) when '.coffee' content_type('text/javascript') body(CoffeeScript.compile(::File.read(file_path))) else extname = File.extname(file_path) type = unless Compiler::FileFinders::BINARY_EXTENSIONS.include?(extname) mime_type = MIME::Types.of(file_path).first if mime_type.nil? 'text/plain' else content_type end end send_file(file_path, type: type, last_modified: mtime) end end |
#handle_server_bade(file_name) ⇒ (Fixnum, String)
42 43 44 |
# File 'lib/epuber/server/handlers.rb', line 42 def handle_server_bade(file_name) handle_bade(File.("pages/#{file_name}", File.dirname(__FILE__))) end |
#handle_websocket(path) ⇒ Object
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
# File 'lib/epuber/server.rb', line 454 def handle_websocket(path) _log :ws, "#{path}: start" request.websocket do |ws| thread = nil ws.onopen do sockets << ws ws.send({ name: :hello }.to_json) thread = Thread.new do loop do sleep(10) ws.send({ name: :heartbeat }.to_json) end end end ws. do |msg| _log :ws, "#{path}: received message: #{msg.inspect}" end ws.onclose do _log :ws, "#{path}: socket closed" sockets.delete(ws) thread.kill end end end |
#handle_xhtml_file(file_path) ⇒ (Fixnum, String)
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/epuber/server/handlers.rb', line 11 def handle_xhtml_file(file_path) html_doc = Nokogiri::XML(File.open(file_path)) add_file_to_head(:js, html_doc, 'vendor/bower/jquery/jquery.min.js') add_file_to_head(:js, html_doc, 'vendor/bower/spin/spin.js') add_file_to_head(:js, html_doc, 'vendor/bower/cookies/cookies.min.js') add_file_to_head(:js, html_doc, 'vendor/bower/uri/URI.min.js') add_file_to_head(:js, html_doc, 'vendor/bower/keymaster/keymaster.js') add_file_to_head(:style, html_doc, 'book_content.styl') add_file_to_head(:js, html_doc, 'support.coffee') (:viewport, 'width=device-width, initial-scale=1.0', html_doc) add_auto_refresh_script(html_doc) unless file_resolver.nil? current_index = file_resolver.spine_files.index { |file| file.final_destination_path == file_path } unless current_index.nil? previous_path = spine_file_at(current_index - 1).try(:pkg_destination_path) next_path = spine_file_at(current_index + 1).try(:pkg_destination_path) add_keyboard_control_script(html_doc, previous_path, next_path) end end [200, html_doc.to_xhtml] end |
#listener ⇒ Listener
101 |
# File 'lib/epuber/server.rb', line 101 instance_class_accessor :listener |
#sockets ⇒ Array<SinatraWebsocket::Connection>
93 |
# File 'lib/epuber/server.rb', line 93 instance_class_accessor :sockets |
#spine ⇒ Array<Epuber::Compiler::File>
89 |
# File 'lib/epuber/server.rb', line 89 instance_class_accessor :spine |
#spine_file_at(index) ⇒ Epuber::Book::File?
223 224 225 226 227 |
# File 'lib/epuber/server.rb', line 223 def spine_file_at(index) return unless !file_resolver.nil? && index >= 0 && index < file_resolver.spine_files.count file_resolver.spine_files[index] end |
#target ⇒ Epuber::Book::Target
77 |
# File 'lib/epuber/server.rb', line 77 instance_class_accessor :target |
#verbose ⇒ FalseClass, TrueClass
81 |
# File 'lib/epuber/server.rb', line 81 instance_class_accessor :verbose |