plezi 0.12.22 → 0.14.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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/LICENSE.txt +17 -18
  4. data/README.md +54 -698
  5. data/Rakefile +7 -4
  6. data/bin/config.ru +22 -0
  7. data/{test → bin}/console +4 -6
  8. data/bin/hello_world +52 -0
  9. data/bin/setup +8 -0
  10. data/exe/plezi +145 -0
  11. data/lib/plezi.rb +24 -137
  12. data/lib/plezi/activation.rb +28 -0
  13. data/lib/plezi/api.rb +62 -0
  14. data/lib/plezi/controller/controller.rb +259 -0
  15. data/lib/plezi/controller/controller_class.rb +176 -0
  16. data/lib/plezi/controller/cookies.rb +40 -0
  17. data/lib/plezi/helpers.rb +60 -0
  18. data/lib/plezi/rake.rb +2 -24
  19. data/lib/plezi/render/erb.rb +34 -0
  20. data/lib/plezi/render/has_cache.rb +36 -0
  21. data/lib/plezi/render/markdown.rb +63 -0
  22. data/lib/plezi/render/render.rb +49 -0
  23. data/lib/plezi/render/sass.rb +55 -0
  24. data/lib/plezi/render/slim.rb +33 -0
  25. data/lib/plezi/router/adclient.rb +23 -0
  26. data/lib/plezi/router/assets.rb +67 -0
  27. data/lib/plezi/router/errors.rb +29 -0
  28. data/lib/plezi/router/route.rb +112 -0
  29. data/lib/plezi/router/router.rb +120 -0
  30. data/lib/plezi/version.rb +1 -1
  31. data/lib/plezi/websockets/message_dispatch.rb +91 -0
  32. data/lib/plezi/websockets/redis.rb +55 -0
  33. data/plezi.gemspec +25 -16
  34. data/resources/404.erb +5 -4
  35. data/resources/500.erb +5 -4
  36. data/resources/{500.html → 503.html} +8 -9
  37. data/resources/client.js +253 -0
  38. data/resources/config.ru +5 -36
  39. data/resources/ctrlr.rb +34 -0
  40. data/resources/gemfile +4 -0
  41. data/resources/mini_app.rb +28 -82
  42. data/resources/mini_exec +7 -0
  43. data/resources/mini_welcome_page.html +0 -0
  44. data/resources/procfile +3 -0
  45. data/resources/rakefile +4 -8
  46. data/resources/routes.rb +9 -26
  47. data/resources/{websockets.js → simple-client.js} +3 -3
  48. metadata +60 -85
  49. data/bin/plezi +0 -104
  50. data/docs/async_helpers.md +0 -245
  51. data/docs/controllers.md +0 -27
  52. data/docs/logging.md +0 -49
  53. data/docs/routes.md +0 -209
  54. data/docs/websockets.md +0 -213
  55. data/lib/plezi/builders/ac_model.rb +0 -59
  56. data/lib/plezi/builders/app_builder.rb +0 -137
  57. data/lib/plezi/builders/builder.rb +0 -43
  58. data/lib/plezi/builders/form_builder.rb +0 -27
  59. data/lib/plezi/common/api.rb +0 -92
  60. data/lib/plezi/common/cache.rb +0 -122
  61. data/lib/plezi/common/defer.rb +0 -21
  62. data/lib/plezi/common/dsl.rb +0 -94
  63. data/lib/plezi/common/redis.rb +0 -65
  64. data/lib/plezi/common/renderer.rb +0 -141
  65. data/lib/plezi/common/settings.rb +0 -52
  66. data/lib/plezi/handlers/controller_core.rb +0 -106
  67. data/lib/plezi/handlers/controller_magic.rb +0 -284
  68. data/lib/plezi/handlers/http_router.rb +0 -205
  69. data/lib/plezi/handlers/placebo.rb +0 -112
  70. data/lib/plezi/handlers/route.rb +0 -216
  71. data/lib/plezi/handlers/session.rb +0 -109
  72. data/lib/plezi/handlers/stubs.rb +0 -156
  73. data/lib/plezi/handlers/ws_identity.rb +0 -253
  74. data/lib/plezi/handlers/ws_object.rb +0 -308
  75. data/lib/plezi/helpers/http_sender.rb +0 -84
  76. data/lib/plezi/helpers/magic_helpers.rb +0 -104
  77. data/lib/plezi/helpers/mime_types.rb +0 -1995
  78. data/lib/plezi/oauth.rb +0 -5
  79. data/lib/plezi/oauth/auth_controller.rb +0 -229
  80. data/logo/dark.png +0 -0
  81. data/logo/light.png +0 -0
  82. data/logo/sign.png +0 -0
  83. data/resources/404.haml +0 -121
  84. data/resources/404.html +0 -124
  85. data/resources/404.slim +0 -120
  86. data/resources/500.haml +0 -120
  87. data/resources/500.slim +0 -120
  88. data/resources/Gemfile +0 -86
  89. data/resources/code.rb +0 -8
  90. data/resources/controller.rb +0 -142
  91. data/resources/database.yml +0 -33
  92. data/resources/db_ac_config.rb +0 -59
  93. data/resources/db_dm_config.rb +0 -51
  94. data/resources/db_sequel_config.rb +0 -33
  95. data/resources/en.yml +0 -204
  96. data/resources/haml_config.rb +0 -6
  97. data/resources/i18n_config.rb +0 -14
  98. data/resources/initialize.rb +0 -49
  99. data/resources/mini_exec.rb +0 -7
  100. data/resources/oauth_config.rb +0 -24
  101. data/resources/plezi_client.js +0 -198
  102. data/resources/plezi_websockets.html +0 -47
  103. data/resources/redis_config.rb +0 -42
  104. data/resources/slim_config.rb +0 -11
  105. data/resources/welcome_page.html +0 -272
  106. data/test/dispatch +0 -58
  107. data/test/hello_world +0 -13
  108. data/test/plezi_tests.rb +0 -581
@@ -0,0 +1,40 @@
1
+ module Plezi
2
+ module Controller
3
+ # The cookie jar class. Controllers have an instance of this class named `cookies`.
4
+ class Cookies < Hash
5
+ attr_reader :request, :response
6
+ def initialize(request, response)
7
+ @request = request
8
+ @response = response
9
+ end
10
+
11
+ # Reads a cookie from either the request cookie Hash or the new cookies Hash.
12
+ def[](key)
13
+ if key.is_a? Symbol
14
+ super(key) || super(key.to_s) || @request.cookies[key] || @request.cookies[key.to_s]
15
+ elsif key.is_a? String
16
+ super(key) || super(key.to_sym) || @request.cookies[key] || @request.cookies[key.to_sym]
17
+ else
18
+ super(key) || @request.cookies[key]
19
+ end
20
+ end
21
+
22
+ # Sets (or deletes) a cookie. New cookies are placed in the new cookie Hash and are accessible only to the controller that created them.
23
+ def[]=(key, value)
24
+ if value.nil?
25
+ @response.delete_cookie key
26
+ delete key
27
+ if key.is_a? Symbol
28
+ delete key.to_s
29
+ elsif key.is_a? String
30
+ delete key.to_sym
31
+ end
32
+ return nil
33
+ end
34
+ @response.set_cookie key, value
35
+ value = value[:value] if value.is_a? Hash
36
+ super
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,60 @@
1
+ require 'plezi/activation'
2
+ require 'plezi/router/router'
3
+
4
+ module Plezi
5
+ module_function
6
+
7
+ # attempts to convert a string's encoding to UTF-8, but only when the string is a valid UTF-8 encoded string.
8
+ def try_utf8!(string, encoding = ::Encoding::UTF_8)
9
+ return nil unless string
10
+ string.force_encoding(::Encoding::ASCII_8BIT) unless string.force_encoding(encoding).valid_encoding?
11
+ string
12
+ end
13
+
14
+ # Sanitizes hash data, attempting to "rubyfy" any Hash Strings to reversible Ruby objects. i.e:
15
+ #
16
+ # {"go" => "<tag>home</tag>", "now" => "false", "count" => "8", "float" => "0.7", "empty" => ""}
17
+ #
18
+ # Will become:
19
+ #
20
+ # {go: "&lt;tag>home&lt;/tag&gt;", now: false, count: 8, float: "0.7", empty: nil}
21
+ #
22
+ # As you may notice, float Strings aren't "rubyfied". Any "rubyfied" object will revert back to the same String form using `to_s`.
23
+ def rubyfy(hash)
24
+ case hash
25
+ when Hash
26
+ cpy = Hash.new(&hash_proc_4symstr)
27
+ hash.each { |k, v| cpy[k.is_a?(String) ? k.to_sym : k] = rubyfy(v) }
28
+ hash = cpy
29
+ when String
30
+ hash = if hash.empty?
31
+ nil
32
+ elsif hash.to_i.to_s == hash
33
+ hash.to_i
34
+ elsif hash == 'true'.freeze
35
+ true
36
+ elsif hash == 'false'.freeze
37
+ false
38
+ else
39
+ ERB::Util.h try_utf8!(hash)
40
+ end
41
+ when Array
42
+ hash = hash.map { |i| rubyfy(i) }
43
+ end
44
+ hash
45
+ end
46
+
47
+ # Catches String/Symbol mixups. Add this to any Hash using Hash#default_proc=
48
+ def hash_proc_4symstr
49
+ @hash_proc_4symstr ||= proc do |hash, key|
50
+ case key
51
+ when String
52
+ tmp = key.to_sym
53
+ hash.key?(tmp) ? hash[tmp] : nil
54
+ when Symbol
55
+ tmp = key.to_s
56
+ hash.key?(tmp) ? hash[tmp] : nil
57
+ end
58
+ end
59
+ end
60
+ end
data/lib/plezi/rake.rb CHANGED
@@ -1,24 +1,2 @@
1
- # make sure this file only runs within the context of a Rake enabled script
2
- if defined? Rake
3
-
4
- # Make sure the server doesn't start
5
- Iodine.protocol = false
6
-
7
-
8
- namespace :make do
9
- # TODO: add ActiveRecord controller-model generator
10
-
11
- # TODO: add Squel controller-model generator
12
-
13
- end
14
- # add console mode
15
- desc "Same as `plezi c`: starts the application as a console, NOT a server."
16
- task :console do
17
- Kernel.exec "plezi c"
18
- end
19
- desc "Same as `rake console`: starts the application as a console, NOT a server."
20
- task :c do
21
- Kernel.exec "plezi c"
22
- end
23
-
24
- end
1
+ require 'plezi'
2
+ Plezi.no_autostart
@@ -0,0 +1,34 @@
1
+ require 'plezi/render/has_cache' unless defined? ::Plezi::Base::HasStore
2
+ require 'erb'
3
+ module Plezi
4
+ module Base
5
+ module RenderERB
6
+ extend ::Plezi::Base::HasStore
7
+
8
+ module_function
9
+
10
+ def call(filename, context, &block)
11
+ return unless defined? ::ERB
12
+ return unless File.exist?(filename)
13
+ engine = load_engine(filename)
14
+ engine.result(context, &block)
15
+ end
16
+ if ENV['RACK_ENV'.freeze] == 'production'.freeze
17
+ def load_engine(filename)
18
+ engine = self[filename]
19
+ return engine if engine
20
+ self[filename] = ::ERB.new(::Plezi.try_utf8!(IO.binread(filename)))
21
+ end
22
+ else
23
+ def load_engine(filename)
24
+ engine, tm = self[filename]
25
+ return engine if engine && (tm == File.mtime(filename))
26
+ self[filename] = [(engine = ::ERB.new(::Plezi.try_utf8!(IO.binread(filename)))), File.mtime(filename)]
27
+ engine
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ ::Plezi::Renderer.register :erb, ::Plezi::Base::RenderERB
@@ -0,0 +1,36 @@
1
+ module Plezi
2
+ module Base
3
+ # Provides a thread-safe caching engine
4
+ module HasCache
5
+ # initializes the cache
6
+ def self.extended(base)
7
+ base.instance_variable_set :@_lock, Mutex.new
8
+ base.instance_variable_set :@_cache, {}.dup
9
+ end
10
+
11
+ # Stores data in the cache
12
+ def store(key, value)
13
+ @_lock.synchronize { @_cache[key] = value }
14
+ end
15
+ alias []= store
16
+ # Retrieves data form the cache
17
+ def get(key)
18
+ @_lock.synchronize { @_cache[key] }
19
+ end
20
+ alias [] get
21
+ end
22
+ # Provides thread-specific caching engine, allowing lockless cache at the expenss of memory.
23
+ module HasStore
24
+ # Stores data in the cache
25
+ def store(key, value)
26
+ (Thread.current[(@_chache_name ||= object_id.to_s(16))] ||= {}.dup)[key] = value
27
+ end
28
+ alias []= store
29
+ # Retrieves data form the cache
30
+ def get(key)
31
+ (Thread.current[(@_chache_name ||= object_id.to_s(16))] ||= {}.dup)[key]
32
+ end
33
+ alias [] get
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,63 @@
1
+ require 'plezi/render/has_cache' unless defined? ::Plezi::Base::HasStore
2
+ # Redcarpet might not be available, if so, allow the require to throw it's exception.
3
+ unless defined?(::Redcarpet::Markdown)
4
+ begin
5
+ require('redcarpet')
6
+ rescue Exception
7
+
8
+ end
9
+ end
10
+
11
+ if defined?(Redcarpet::Markdown)
12
+ module Plezi
13
+ module Base
14
+ module RenderMarkDown
15
+ # A custom render engine that makes sure links to foriegn sites open in a new window/tab.
16
+ class NewPageLinksMDRenderer < Redcarpet::Render::HTML
17
+ # review's the link and renders the Html
18
+ def link(link, title, content)
19
+ "<a href=\"#{link}\"#{" target='_blank'" if link =~ /^http[s]?\:\/\//}#{" title=\"#{title}\"" if title}>#{content}</a>"
20
+ end
21
+ end
22
+
23
+ # Extensions
24
+ MD_EXTENSIONS = { with_toc_data: true, strikethrough: true, autolink: true, fenced_code_blocks: true, no_intra_emphasis: true, tables: true, footnotes: true, underline: true, highlight: true }.freeze
25
+ # create a single gloabl renderer for all markdown files.
26
+ MD_RENDERER = Redcarpet::Markdown.new NewPageLinksMDRenderer.new(MD_EXTENSIONS.dup), MD_EXTENSIONS.dup
27
+
28
+ # create a single gloabl renderer for all markdown TOC.
29
+ MD_RENDERER_TOC = Redcarpet::Markdown.new Redcarpet::Render::HTML_TOC.new(MD_EXTENSIONS.dup), MD_EXTENSIONS.dup
30
+
31
+ extend ::Plezi::Base::HasStore
32
+
33
+ module_function
34
+
35
+ # renders the markdown file, if exists
36
+ def call(filename, _context)
37
+ return unless File.exist?(filename)
38
+ load_engine(filename)
39
+ end
40
+
41
+ if ENV['RACK_ENV'.freeze] == 'production'.freeze
42
+ # loads the rendered cache
43
+ def load_engine(filename)
44
+ engine = self[filename]
45
+ return engine if engine
46
+ data = IO.read filename
47
+ self[filename] = "<div class='toc'>#{::Plezi::Base::RenderMarkDown::MD_RENDERER_TOC.render(data)}</div>\n#{::Plezi::Base::RenderMarkDown::MD_RENDERER.render(data)}"
48
+ end
49
+ else
50
+ # loads the rendered cache
51
+ def load_engine(filename)
52
+ engine, tm = self[filename]
53
+ return engine if engine && tm == File.mtime(filename)
54
+ data = IO.read filename
55
+ (self[filename] = ["<div class='toc'>#{::Plezi::Base::RenderMarkDown::MD_RENDERER_TOC.render(data)}</div>\n#{::Plezi::Base::RenderMarkDown::MD_RENDERER.render(data)}", File.mtime(filename)])[0]
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ ::Plezi::Renderer.register :md, ::Plezi::Base::RenderMarkDown
63
+ end
@@ -0,0 +1,49 @@
1
+ require 'thread'
2
+
3
+ module Plezi
4
+ module Renderer
5
+ @render_library = {}.dup
6
+
7
+ public
8
+
9
+ module_function
10
+
11
+ # Registers a rendering extention.
12
+ #
13
+ # Slim, Markdown, ERB and SASS are registered by default.
14
+ #
15
+ # extention:: a Symbol or String representing the extention of the file to be rendered. i.e. 'slim', 'md', 'erb', etc'
16
+ # handler :: a Proc or other object that answers to call(filename, context, &block) and returnes the rendered string.
17
+ # The block accepted by the handler is for chaining rendered actions (allowing for `yield` within templates)
18
+ # and the context is the object within which the rendering should be performed (if `binding` handling is
19
+ # supported by the engine). `filename` might not point to an existing or valid file.
20
+ #
21
+ # If a block is passed to the `register_hook` method with no handler defined, it will act as the handler.
22
+ def register(extention, handler = nil, &block)
23
+ handler ||= block
24
+ raise 'Handler or block required.' unless handler
25
+ @render_library[extention.to_s] = handler
26
+ handler
27
+ end
28
+
29
+ # Removes a registered render extention
30
+ def remove(extention)
31
+ @render_library.delete extention.to_s
32
+ end
33
+
34
+ # Attempts to render the requested file (i.e. `'index.html'`) using all known rendering handlers.
35
+ def render(base_filename, context = (Object.new.instance_eval { binding }), &block)
36
+ ret = nil
37
+ @render_library.each { |ext, handler| ret = handler.call("#{base_filename}.#{ext}".freeze, context, &block); return ret if ret; }
38
+ ret
39
+ end
40
+ # Renderer.register :haml do |filename, context, &block|
41
+ # next unless defined? ::Haml
42
+ # (Plezi.cache_needs_update?(filename) ? Plezi.cache_data(filename, Haml::Engine.new(Plezi::Base::Helpers.try_utf8!(IO.binread(filename)))) : (Plezi.get_cached filename)).render(context.receiver, &block)
43
+ # end
44
+ end
45
+ end
46
+
47
+ require 'plezi/render/slim.rb'
48
+ require 'plezi/render/markdown.rb'
49
+ require 'plezi/render/erb.rb'
@@ -0,0 +1,55 @@
1
+ require 'thread'
2
+ require 'plezi/render/has_cache'
3
+ # Redcarpet might not be available, if so, allow the require to throw it's exception.
4
+ unless defined?(::Sass)
5
+ begin
6
+ require('sass')
7
+ rescue Exception
8
+
9
+ end
10
+ end
11
+
12
+ if defined?(::Sass)
13
+
14
+ module Plezi
15
+ module Base
16
+ # This is a baker, not a renderer
17
+ module BakeSASS
18
+ extend HasCache
19
+
20
+ module_function
21
+
22
+ SASS_OPTIONS = { cache_store: Sass::CacheStores::Memory.new, style: (ENV['SASS_STYLE'] || ((ENV['ENV'] || ENV['RACK_ENV']) == 'production' ? :compressed : :nested)) }.dup
23
+
24
+ # Bakes the SASS for the requested target, if a SASS source file is found.
25
+ def call(target)
26
+ return self[target] if File.extname(target) == '.map'.freeze
27
+ review_cache("#{target}.scss", target) || review_cache("#{target}.sass", target)
28
+ end
29
+
30
+ def review_cache(filename, target)
31
+ return nil unless File.exist?(filename)
32
+ eng = self[filename]
33
+ return true unless eng.nil? || refresh_sass?(filename)
34
+ self[filename] = (eng = Sass::Engine.for_file(filename, SASS_OPTIONS)).dependencies
35
+ map_name = "#{target}.map".freeze
36
+ css, map = eng.render_with_sourcemap(File.basename(map_name))
37
+ self[filename.to_sym] = Time.now
38
+ IO.write map_name, map.to_json(css_uri: File.basename(target))
39
+ self[target] = css
40
+ end
41
+
42
+ def refresh_sass?(sass)
43
+ mt = self[sass.to_sym]
44
+ return true if mt < File.mtime(sass)
45
+ self[sass].each { |e| return true if File.exist?(e.options[:filename]) && (File.mtime(e.options[:filename]) > mt) }
46
+ false
47
+ end
48
+ end
49
+
50
+ ::Plezi::AssetBaker.register :css, ::Plezi::Base::BakeSASS
51
+ ::Plezi::AssetBaker.register :map, ::Plezi::Base::BakeSASS
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,33 @@
1
+ require 'plezi/render/has_cache' unless defined? ::Plezi::Base::HasStore
2
+ module Plezi
3
+ module Base
4
+ module RenderSlim
5
+ extend ::Plezi::Base::HasStore
6
+
7
+ module_function
8
+
9
+ def call(filename, context, &block)
10
+ return unless defined? ::Slim
11
+ return unless File.exist?(filename)
12
+ engine = load_engine(filename)
13
+ engine.render(context.receiver, &block)
14
+ end
15
+ if ENV['RACK_ENV'.freeze] == 'production'.freeze
16
+ def load_engine(filename)
17
+ engine = self[filename]
18
+ return engine if engine
19
+ self[filename] = (Slim::Template.new { ::Plezi.try_utf8!(IO.binread(filename)) })
20
+ end
21
+ else
22
+ def load_engine(filename)
23
+ engine, tm = self[filename]
24
+ return engine if engine && (tm == File.mtime(filename))
25
+ self[filename] = [(engine = Slim::Template.new { ::Plezi.try_utf8!(IO.binread(filename)) }), File.mtime(filename)]
26
+ engine
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ ::Plezi::Renderer.register :slim, ::Plezi::Base::RenderSlim
@@ -0,0 +1,23 @@
1
+ require 'plezi/router/route'
2
+ require 'plezi/router/errors'
3
+ require 'plezi/router/assets'
4
+ require 'rack'
5
+
6
+ module Plezi
7
+ module Base
8
+ module Router
9
+ class ADClient
10
+ def index
11
+ fname = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'resources', 'client.js'))
12
+ response.body = File.open(fname)
13
+ response['X-Sendfile'] = fname
14
+ true
15
+ end
16
+
17
+ def show
18
+ index
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,67 @@
1
+ require 'fileutils'
2
+ require 'set'
3
+ module Plezi
4
+ module Base
5
+ class Assets
6
+ if ENV['RACK_ENV'.freeze] == 'production'.freeze
7
+ def index
8
+ name = File.join(Plezi.assets, *params['*'.freeze]).freeze
9
+ data = ::Plezi::AssetBaker.bake(name)
10
+ return false unless data
11
+ name = File.join(Iodine::Rack.public, request.path_info[1..-1]).freeze if Iodine::Rack.public
12
+ if data.is_a?(String)
13
+ FileUtils.mkpath File.dirname(name)
14
+ IO.binwrite(name, data)
15
+ end
16
+ response['X-Sendfile'.freeze] = name
17
+ response.body = File.open(name)
18
+ true
19
+ end
20
+ else
21
+ def index
22
+ name = File.join(Plezi.assets, *params['*'.freeze]).freeze
23
+ data = ::Plezi::AssetBaker.bake(name)
24
+ IO.binwrite(name, data) if data.is_a?(String)
25
+ if File.exist? name
26
+ response['X-Sendfile'.freeze] = name
27
+ response.body = File.open(name)
28
+ return true
29
+ end
30
+ false
31
+ end
32
+ end
33
+
34
+ def show
35
+ index
36
+ end
37
+ end
38
+ end
39
+ # This module is used for asset "baking" (or "belated baking"). It allows us to easily register and support new types of assets.
40
+ module AssetBaker
41
+ @drivers = {}
42
+ # Registers a new Asset Driver of a specific extension (i.e. "css", "js", etc')
43
+ #
44
+ # Multiple Asset Drivers can be registered for the same extension. The will be attempted in the order of their registration.
45
+ #
46
+ # An Asset Drivers is an object that responsd to `.call(target)`.
47
+ # If the traget is newly rendered, the driver should return the rendered text.
48
+ # If the asset didn't change since the last time `.call(target)` was called, the driver should return 'true' (meanning, yet, the asset exists, it's the same).
49
+ # If the driver doesn't locate the asset, it should return `nil` or `false`, indicating the next driver should be attempted.
50
+ def self.register(ext, driver)
51
+ (@drivers[".#{ext}".freeze] ||= [].to_set) << driver
52
+ end
53
+
54
+ # @private
55
+ # called by Plezi when in need of baking an asset.
56
+ def self.bake(name)
57
+ ret = nil
58
+ ext = File.extname name
59
+ return false if ext.empty?
60
+ driver = @drivers[ext]
61
+ return false if driver.nil?
62
+ driver.each { |d| ret = d.call(name); return ret if ret }
63
+ nil
64
+ end
65
+ end
66
+ end
67
+ require 'plezi/render/sass.rb'