plezi 0.12.22 → 0.14.0

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