plezi 0.9.2 → 0.10.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +44 -31
  4. data/bin/plezi +3 -3
  5. data/lib/plezi.rb +21 -43
  6. data/lib/plezi/common/defer.rb +21 -0
  7. data/lib/plezi/common/dsl.rb +115 -91
  8. data/lib/plezi/common/redis.rb +44 -0
  9. data/lib/plezi/common/settings.rb +58 -0
  10. data/lib/plezi/handlers/controller_core.rb +132 -0
  11. data/lib/plezi/handlers/controller_magic.rb +85 -259
  12. data/lib/plezi/handlers/http_router.rb +139 -60
  13. data/lib/plezi/handlers/route.rb +9 -178
  14. data/lib/plezi/handlers/stubs.rb +2 -2
  15. data/lib/plezi/helpers/http_sender.rb +72 -0
  16. data/lib/plezi/helpers/magic_helpers.rb +12 -0
  17. data/lib/plezi/{server → helpers}/mime_types.rb +0 -0
  18. data/lib/plezi/version.rb +1 -1
  19. data/plezi.gemspec +3 -11
  20. data/resources/Gemfile +20 -21
  21. data/resources/controller.rb +2 -2
  22. data/resources/oauth_config.rb +1 -1
  23. data/resources/redis_config.rb +2 -0
  24. data/test/plezi_tests.rb +39 -46
  25. metadata +24 -33
  26. data/lib/plezi/common/logging.rb +0 -60
  27. data/lib/plezi/eventmachine/connection.rb +0 -190
  28. data/lib/plezi/eventmachine/em.rb +0 -98
  29. data/lib/plezi/eventmachine/io.rb +0 -272
  30. data/lib/plezi/eventmachine/protocol.rb +0 -54
  31. data/lib/plezi/eventmachine/queue.rb +0 -51
  32. data/lib/plezi/eventmachine/ssl_connection.rb +0 -144
  33. data/lib/plezi/eventmachine/timers.rb +0 -117
  34. data/lib/plezi/eventmachine/workers.rb +0 -33
  35. data/lib/plezi/handlers/http_echo.rb +0 -27
  36. data/lib/plezi/handlers/http_host.rb +0 -214
  37. data/lib/plezi/handlers/magic_helpers.rb +0 -32
  38. data/lib/plezi/server/http.rb +0 -129
  39. data/lib/plezi/server/http_protocol.rb +0 -319
  40. data/lib/plezi/server/http_request.rb +0 -146
  41. data/lib/plezi/server/http_response.rb +0 -319
  42. data/lib/plezi/server/websocket.rb +0 -251
  43. data/lib/plezi/server/websocket_client.rb +0 -178
  44. data/lib/plezi/server/ws_response.rb +0 -161
@@ -1,54 +0,0 @@
1
- module Plezi
2
-
3
-
4
- module EventMachine
5
-
6
- # this module is the protocol (controller) for the HTTP server.
7
- #
8
- #
9
- # to do: implemet logging, support body types: multipart (non-ASCII form data / uploaded files), json & xml
10
- class Protocol
11
-
12
- attr_accessor :connection, :params
13
- attr_reader :buffer, :locker
14
-
15
- # initializes the protocol object
16
- def initialize connection, params
17
- @buffer, @connection, @params, @locker = [], connection, params, connection.locker
18
- end
19
-
20
- # called when connection is initialized.
21
- def on_connect
22
- end
23
-
24
- # called after data is recieved.
25
- #
26
- # this method is called within a lock on the connection (Mutex) - craeful from double locking.
27
- def on_message
28
- end
29
-
30
- # # called when a disconnect is fired
31
- # # (socket was disconnected / service should be disconnected / shutdown / socket error)
32
- def on_disconnect
33
- end
34
-
35
- # called when an exception was raised
36
- # (socket was disconnected / service should be disconnected / shutdown / socket error)
37
- def on_exception
38
- EventMachine.remove_io connection.io
39
- Plezi.error e
40
- end
41
- end
42
- end
43
- end
44
-
45
- ## Heroku/extra headers info
46
-
47
- # All headers are considered to be case-insensitive, as per HTTP Specification.
48
- # X-Forwarded-For: the originating IP address of the client connecting to the Heroku router
49
- # X-Forwarded-Proto: the originating protocol of the HTTP request (example: https)
50
- # X-Forwarded-Port: the originating port of the HTTP request (example: 443)
51
- # X-Request-Start: unix timestamp (milliseconds) when the request was received by the router
52
- # X-Request-Id: the Heroku HTTP Request ID
53
- # Via: a code name for the Heroku router
54
-
@@ -1,51 +0,0 @@
1
- module Plezi
2
- module EventMachine
3
- module_function
4
-
5
- QUEUE = []
6
- QUEUE_LOCKER = Mutex.new
7
-
8
- def queue args, job
9
- raise "Job missing" unless job
10
- QUEUE_LOCKER.synchronize { QUEUE << [job, args]}
11
- true
12
- end
13
-
14
- def do_job
15
- job, args = QUEUE_LOCKER.synchronize { QUEUE.shift }
16
- job ? (job.call(*args) || true) : false
17
- end
18
-
19
- def queue_empty?
20
- QUEUE.empty?
21
- end
22
-
23
- end
24
-
25
- module_function
26
-
27
- # Accepts a block and runs it asynchronously. This method runs asynchronously and returns immediately.
28
- #
29
- # use:
30
- #
31
- # Plezi.run_async(arg1, arg2, arg3 ...) { |arg1, arg2, arg3...| do_something }
32
- #
33
- # the block will be run within the current context, allowing access to current methods and variables.
34
- def run_async *args, &block
35
- EventMachine.queue args, block
36
- end
37
-
38
- # This method runs asynchronously and returns immediately.
39
- #
40
- # This method accepts:
41
- # object:: an object who's method will be called.
42
- # method:: the method's name to be called. type: Symbol.
43
- # *args:: any arguments to be passed to the method.
44
- #
45
- # This method also accepts an optional block which will be run with the method's returned value within the existing context.
46
- #
47
- def callback object, method, *args, &block
48
- block ? EventMachine.queue( args, (proc { |ar| block.call( object.method(method).call(*ar) ) }) ) : EventMachine.queue(args, object.method(method) )
49
- end
50
-
51
- end
@@ -1,144 +0,0 @@
1
- # encoding: UTF-8
2
-
3
-
4
-
5
- module Plezi
6
-
7
- module EventMachine
8
-
9
- # represents an SSL connection
10
- class SSLConnection < Connection
11
- #the SSL socket
12
- attr_reader :ssl_socket
13
-
14
- def initialize socket, params
15
- if params[:ssl_client]
16
- context = OpenSSL::SSL::SSLContext.new
17
- context.set_params verify_mode: OpenSSL::SSL::VERIFY_NONE # OpenSSL::SSL::VERIFY_PEER #OpenSSL::SSL::VERIFY_NONE
18
- @ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, context)
19
- @ssl_socket.sync_close = true
20
- @ssl_socket.connect
21
- elsif params[:ssl] || params[:ssl_key] || params[:ssl_cert]
22
- params[:ssl_cert], params[:ssl_key] = SSLConnection.self_cert unless params[:ssl_key] && params[:ssl_cert]
23
- context = OpenSSL::SSL::SSLContext.new
24
- context.set_params verify_mode: OpenSSL::SSL::VERIFY_NONE # OpenSSL::SSL::VERIFY_PEER #OpenSSL::SSL::VERIFY_NONE
25
- # context.options DoNotReverseLookup: true
26
- context.cert, context.key = params[:ssl_cert], params[:ssl_key]
27
- context.cert_store = OpenSSL::X509::Store.new
28
- context.cert_store.set_default_paths
29
- @ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, context)
30
- @ssl_socket.sync_close = true
31
- @ssl_socket.accept
32
- end
33
- raise "Not an SSL connection or SSL Socket creation failed" unless @ssl_socket
34
- super
35
- end
36
-
37
-
38
- # returns an IO-like object used for reading/writing (unlike the original IO object, this can be an SSL layer or any other wrapper object).
39
- def io
40
- @ssl_socket
41
- end
42
-
43
- # makes sure any data in the que is send and calls `flush` on the socket, to make sure the buffer is sent.
44
- def flush
45
- begin
46
- send
47
- @ssl_socket.flush
48
- rescue => e
49
-
50
- end
51
- end
52
-
53
- # returns true if the service is disconnected
54
- def disconnected?
55
- (@socket.closed? || @ssl_socket.closed? || @socket.stat.mode != 0140666) rescue true # if mode is read only, it's the same as closed.
56
- end
57
-
58
- # identification markers
59
-
60
- #returns the service type - set to normal
61
- def service_type
62
- 'ssl'
63
- end
64
- #returns true if the service is encrypted using the OpenSSL library.
65
- def ssl?
66
- true
67
- end
68
-
69
- #################
70
- # overide the followind methods for any child class.
71
-
72
- # this is a public method and it should be used by child classes to implement each
73
- # read(_nonblock) action. accepts one argument ::size for an optional buffer size to be read.
74
- def read size = 1048576
75
- data = ''
76
- begin
77
- (data << @ssl_socket.read_nonblock(size).to_s) until data.bytesize >= size
78
- rescue => e
79
-
80
- end
81
- return false if data.to_s.empty?
82
- touch
83
- data
84
- end
85
-
86
- protected
87
-
88
- # this is a protected method, it should be used by child classes to implement each
89
- # send action.
90
- def _send data
91
- @active_time += 7200
92
- @ssl_socket.write data
93
- touch
94
- end
95
-
96
- public
97
-
98
- # SSL certificate
99
-
100
- # returns the current self-signed certificate - or creates a new one, if there is no current certificate.
101
- def self.self_cert bits=2048, cn=nil, comment='a self signed certificate for when we only need encryption and no more.'
102
- @@self_cert ||= create_cert
103
- return *@@self_cert
104
- end
105
- #creates a self-signed certificate
106
- def self.create_cert bits=2048, cn=nil, comment='a self signed certificate for when we only need encryption and no more.'
107
- unless cn
108
- host_name = Socket::gethostbyname(Socket::gethostname)[0].split('.')
109
- cn = ''
110
- host_name.each {|n| cn << "/DC=#{n}"}
111
- cn << "/CN=#{host_name.join('.')}"
112
- end
113
- # cn ||= "CN=#{Socket::gethostbyname(Socket::gethostname)[0] rescue Socket::gethostname}"
114
-
115
- rsa = OpenSSL::PKey::RSA.new(bits)
116
- cert = OpenSSL::X509::Certificate.new
117
- cert.version = 2
118
- cert.serial = 1
119
- name = OpenSSL::X509::Name.parse(cn)
120
- cert.subject = name
121
- cert.issuer = name
122
- cert.not_before = Time.now
123
- cert.not_after = Time.now + (365*24*60*60)
124
- cert.public_key = rsa.public_key
125
-
126
- ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
127
- ef.issuer_certificate = cert
128
- cert.extensions = [
129
- ef.create_extension("basicConstraints","CA:FALSE"),
130
- ef.create_extension("keyUsage", "keyEncipherment"),
131
- ef.create_extension("subjectKeyIdentifier", "hash"),
132
- ef.create_extension("extendedKeyUsage", "serverAuth"),
133
- ef.create_extension("nsComment", comment),
134
- ]
135
- aki = ef.create_extension("authorityKeyIdentifier",
136
- "keyid:always,issuer:always")
137
- cert.add_extension(aki)
138
- cert.sign(rsa, OpenSSL::Digest::SHA1.new)
139
-
140
- return cert, rsa
141
- end
142
- end
143
- end
144
- end
@@ -1,117 +0,0 @@
1
- module Plezi
2
- module EventMachine
3
-
4
- # Every timed event is a member of the TimedEvent class and responds to it's methods.
5
- class TimedEvent
6
-
7
- # Sets/gets how often a timed event repeats, in seconds.
8
- attr_accessor :interval
9
- # Sets/gets how many times a timed event repeats.
10
- # If set to false or -1, the timed event will repead until the application quits.
11
- attr_accessor :repeat_limit
12
-
13
- # Initialize a timed event.
14
- def initialize interval, repeat_limit = -1, args=[], job=nil
15
- @interval = interval
16
- @repeat_limit = repeat_limit ? repeat_limit.to_i : -1
17
- @job = job || (Proc.new { stop! })
18
- @next = Time.now + interval
19
- @args = args
20
- end
21
-
22
- # stops a timed event.
23
- def stop!
24
- @repeat_limit = 0
25
- end
26
-
27
- # Returns true if the timer is finished.
28
- #
29
- # If the timed event is due, this method will also add the event to the queue.
30
- def done?(time = Time.now)
31
- return false unless @next <= time
32
- return true if @repeat_limit == 0
33
- @repeat_limit -= 1 if @repeat_limit.to_i > 0
34
- EventMachine.queue @args, @job
35
- @next = time + @interval
36
- @repeat_limit == 0
37
- end
38
- end
39
-
40
- module_function
41
-
42
- # the timers stack.
43
- TIMERS = []
44
- # the timers stack Mutex.
45
- TIMERS_LOCK = Mutex.new
46
-
47
- # Creates a TimedEvent object and adds it to the Timers stack.
48
- def timed_job seconds, limit = false, args = [], block = nil
49
- TIMERS_LOCK.synchronize {TIMERS << TimedEvent.new(seconds, limit, args, block); TIMERS.last}
50
- end
51
-
52
- # returns true if there are any unhandled events
53
- def timers?
54
- TIMERS.any?
55
- end
56
-
57
- # cycles through timed jobs, executing and/or deleting them if their time has come.
58
- def fire_timers
59
- TIMERS_LOCK.synchronize { time = Time.now; TIMERS.delete_if {|t| t.done? time} }
60
- end
61
- # clears all timers
62
- def clear_timers
63
- TIMERS.clear
64
- end
65
-
66
- end
67
-
68
- module_function
69
-
70
- # # returns true if there are any unhandled events
71
- # def timers?
72
- # EventMachine.timers?
73
- # end
74
-
75
- # pushes a timed event to the timers's stack
76
- #
77
- # accepts:
78
- # seconds:: the minimal amount of seconds to wait before calling the handler's `call` method.
79
- # *arg:: any arguments that will be passed to the handler's `call` method.
80
- # &block:: the block to execute.
81
- #
82
- # A block is required.
83
- #
84
- # Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistant).
85
- def run_after seconds, *args, &block
86
- EventMachine.timed_job seconds, 1, args, block
87
- end
88
-
89
- # pushes a timed event to the timers's stack
90
- #
91
- # accepts:
92
- # time:: the time at which the job should be executed.
93
- # *arg:: any arguments that will be passed to the handler's `call` method.
94
- # &block:: the block to execute.
95
- #
96
- # A block is required.
97
- #
98
- # Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistant).
99
-
100
- def run_at time, *args, &block
101
- EventMachine.timed_job( (Time.now - time), 1, args, block)
102
- end
103
- # pushes a repeated timed event to the timers's stack
104
- #
105
- # accepts:
106
- # seconds:: the minimal amount of seconds to wait before calling the handler's `call` method.
107
- # limit:: the amount of times the event should repeat itself. The event will repeat every x amount of `seconds`. The event will repeat forever if limit is set to false.
108
- # *arg:: any arguments that will be passed to the handler's `call` method.
109
- # &block:: the block to execute.
110
- #
111
- # A block is required.
112
- #
113
- # Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistant).
114
- def run_every seconds, limit = -1, *args, &block
115
- EventMachine.timed_job seconds, limit, args, block
116
- end
117
- end
@@ -1,33 +0,0 @@
1
- module Plezi
2
- module EventMachine
3
-
4
- # A single worker.
5
- class Worker
6
- def initialize
7
- @stop = false
8
- wait = Worker.get_wait
9
- @thread = Thread.new { EventMachine.run wait until @stop }
10
- end
11
- def stop
12
- @instances = -1
13
- @stop = true
14
- end
15
- def join
16
- stop
17
- @thread.join rescue true
18
- end
19
- def alive?
20
- @thread.alive?
21
- end
22
- def status
23
- @thread.status
24
- end
25
- def self.get_wait
26
- @primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
27
- @instances ||= -1
28
- @instances += 1 if @instances < 7
29
- @primes[@instances] / 10.0
30
- end
31
- end
32
- end
33
- end
@@ -1,27 +0,0 @@
1
- module Plezi
2
- #####
3
- # this is a Handler stub class for an HTTP echo server.
4
- module HTTPEcho
5
- module_function
6
-
7
- # handles requests by printing out the parsed data. gets the `request` parameter from the HTTP protocol.
8
- def on_request request
9
- response = HTTPResponse.new request, 200, {'content-type' => 'text/plain'}, ["parsed as:\r\n", request.to_s]
10
- response.body.last << "\n\n params:"
11
- request.params.each {|k,v| response.body.last << "\n#{k}: #{v}"}
12
- response.send
13
- response.finish
14
- end
15
-
16
- # does nothing - a simple stub as required from handlers
17
- def add_route *args
18
- self
19
- end
20
-
21
- # does nothing - a simple stub as required from handlers
22
- def add_host *args
23
- self
24
- end
25
- end
26
-
27
- end
@@ -1,214 +0,0 @@
1
- module Plezi
2
- #####
3
- # this is a Handler stub class for an HTTP echo server.
4
- class HTTPHost
5
-
6
- # the parameters / settings for the Host.
7
- attr_reader :params
8
- # the routing array
9
- attr_reader :routes
10
-
11
- # initializes an HTTP host with the parameters for the specific host.
12
- #
13
- # parameters are the same (almost) as `add_service` and include `root` for file root, `assets`
14
- # and other non service related options.
15
- def initialize params = {}
16
- @params = params.dup
17
- @routes = []
18
- # params[:save_assets] = true unless params[:save_assets] == false
19
- @params[:index_file] ||= 'index.html'
20
- @params[:assets_public] ||= '/assets'
21
- @params[:assets_public].chomp! '/'
22
-
23
- @sass_cache = Sass::CacheStores::Memory.new if defined?(::Sass)
24
- # @sass_cache_lock = Mutex.new
25
- end
26
-
27
- # adds a route under the specific host
28
- def add_route path, controller, &block
29
- routes << Route.new(path, controller, params, &block)
30
- end
31
-
32
- # handles requests sent to the host. returns true if the host delt with the request.
33
- #
34
- # since hosts are required to handle the requests (send 404 errors if resources arrn't found),
35
- # this method always returns true.
36
- def on_request request
37
- begin
38
- # render any assets?
39
- return true if render_assets request
40
-
41
- # send static file, if exists and root is set.
42
- return true if send_static_file request
43
-
44
- # return if a route answered the request
45
- routes.each {|r| return true if r.on_request(request) }
46
-
47
- # send folder listing if root is set, directory listing is set and folder exists
48
-
49
- #to-do
50
-
51
- #return error code or 404 not found
52
- send_by_code request, 404
53
- rescue Exception => e
54
- # return 500 internal server error.
55
- Plezi.error e
56
- send_by_code request, 500
57
- end
58
- true
59
- end
60
-
61
- # Dresses up as a Rack app (If you don't like WebSockets, it's a reasonable aaproach).
62
- def call request
63
- request = Rack::Request.new request if defined? Rack
64
- ret = nil
65
- begin
66
- # render any assets?
67
- ret = render_assets request
68
- return ret if ret
69
-
70
- # send static file, if exists and root is set.
71
- ret = send_static_file request
72
- return ret if ret
73
-
74
- # return if a route answered the request
75
- routes.each {|r| ret = r.call(request); return ret if ret }
76
-
77
- # send folder listing if root is set, directory listing is set and folder exists
78
-
79
- #to-do
80
-
81
- #return error code or 404 not found
82
- return send_by_code request, 404
83
- rescue Exception => e
84
- # return 500 internal server error.
85
- Plezi.error e
86
- return send_by_code request, 500
87
- end
88
- true
89
- end
90
-
91
- ################
92
- ## basic responses
93
- ## (error codes and static files)
94
-
95
- # sends a response for an error code, rendering the relevent file (if exists).
96
- def send_by_code request, code, headers = {}
97
- begin
98
- @base_code_path ||= params[:templates] || File.expand_path('.')
99
- if defined?(::Slim) && Plezi.file_exists?(fn = File.join(@base_code_path, "#{code}.html.slim"))
100
- Plezi.cache_data fn, Slim::Template.new( fn ) unless Plezi.cached? fn
101
- return send_raw_data request, Plezi.get_cached( fn ).render( self, request: request ), 'text/html', code, headers
102
- elsif defined?(::Haml) && Plezi.file_exists?(fn = File.join(@base_code_path, "#{code}.html.haml"))
103
- Plezi.cache_data fn, Haml::Engine.new( IO.read( fn ) ) unless Plezi.cached? fn
104
- return send_raw_data request, Plezi.get_cached( File.join(@base_code_path, "#{code}.html.haml") ).render( self ), 'text/html', code, headers
105
- elsif defined?(::ERB) && Plezi.file_exists?(fn = File.join(@base_code_path, "#{code}.html.erb"))
106
- return send_raw_data request, ERB.new( Plezi.load_file( fn ) ).result(binding), 'text/html', code, headers
107
- elsif Plezi.file_exists?(fn = File.join(@base_code_path, "#{code}.html"))
108
- return send_file(request, fn, code, headers)
109
- end
110
- return true if send_raw_data(request, HTTPResponse::STATUS_CODES[code], 'text/plain', code, headers)
111
- rescue Exception => e
112
- Plezi.error e
113
- end
114
- false
115
- end
116
-
117
- # attempts to send a static file by the request path (using `send_file` and `send_raw_data`).
118
- #
119
- # returns true if data was sent.
120
- def send_static_file request
121
- return false unless params[:root]
122
- file_requested = request[:path].to_s.split('/')
123
- unless file_requested.include? '..'
124
- file_requested.shift
125
- file_requested = File.join(params[:root], *file_requested)
126
- return true if send_file request, file_requested
127
- return send_file request, File.join(file_requested, params[:index_file])
128
- end
129
- false
130
- end
131
-
132
- # sends a file/cacheed data if it exists. otherwise returns false.
133
- def send_file request, filename, status_code = 200, headers = {}
134
- if Plezi.file_exists?(filename) && !::File.directory?(filename)
135
- return send_raw_data request, Plezi.load_file(filename), MimeTypeHelper::MIME_DICTIONARY[::File.extname(filename)], status_code, headers
136
- end
137
- return false
138
- end
139
- # sends raw data through the connection. always returns true (data send).
140
- def send_raw_data request, data, mime, status_code = 200, headers = {}
141
- response = HTTPResponse.new request, status_code, headers
142
- response['cache-control'] = 'public, max-age=86400'
143
- response << data
144
- response['content-length'] = data.bytesize
145
- response.finish
146
- true
147
- end
148
-
149
- ###############
150
- ## asset rendering and responses
151
-
152
- # renders assets, if necessary, and places the rendered result in the cache and in the public folder.
153
- def render_assets request
154
- # contine only if assets are defined and called for
155
- return false unless @params[:assets] && request.path.match(/^#{params[:assets_public]}\/.+/)
156
- # review callback, if defined
157
- return true if params[:assets_callback] && params[:assets_callback].call(request)
158
-
159
- # get file requested
160
- source_file = File.join(params[:assets], *(request.path.match(/^#{params[:assets_public]}\/(.+)/)[1].split('/')))
161
-
162
- # stop if file name is reserved / has security issues
163
- return false if source_file.match(/(scss|sass|coffee|\.\.\/)$/)
164
-
165
- # set where to store the rendered asset
166
- target_file = false
167
- target_file = File.join( params[:root], params[:assets_public], *request.path.match(/^#{params[:assets_public]}\/(.*)/)[1].split('/') ) if params[:root]
168
-
169
- # send the file if it exists (no render needed)
170
- if File.exists?(source_file)
171
- data = Plezi.cache_needs_update?(source_file) ? Plezi.save_file(target_file, Plezi.reload_file(source_file), params[:save_assets]) : Plezi.load_file(source_file)
172
- return (data ? send_raw_data(request, data, MimeTypeHelper::MIME_DICTIONARY[::File.extname(source_file)]) : false)
173
- end
174
-
175
- # render supported assets
176
- case source_file
177
- when /\.css$/
178
- sass = source_file.gsub /css$/, 'sass'
179
- sass.gsub! /sass$/, 'scss' unless Plezi.file_exists?(sass)
180
- return false unless Plezi.file_exists?(sass)
181
- # review mtime and render sass if necessary
182
- if defined?(::Sass) && refresh_sass?(sass)
183
- eng = Sass::Engine.for_file(sass, cache_store: @sass_cache)
184
- Plezi.cache_data sass, eng.dependencies
185
- css, map = eng.render_with_sourcemap(params[:assets_public])
186
- Plezi.save_file target_file, css, params[:save_assets]
187
- Plezi.save_file (target_file + ".map"), map, params[:save_assets]
188
- end
189
- # try to send the cached css file which started the request.
190
- return send_file request, target_file
191
- when /\.js$/
192
- coffee = source_file.gsub /js$/i, 'coffee'
193
- return false unless Plezi.file_exists?(coffee)
194
- # review mtime and render coffee if necessary
195
- if defined?(::CoffeeScript) && Plezi.cache_needs_update?(coffee)
196
- # render coffee to cache
197
- Plezi.cache_data coffee, nil
198
- Plezi.save_file target_file, CoffeeScript.compile(IO.read coffee), params[:save_assets]
199
- end
200
- # try to send the cached js file which started the request.
201
- return send_file request, target_file
202
- end
203
- false
204
- end
205
- def refresh_sass? sass
206
- return false unless File.exists?(sass)
207
- return true if Plezi.cache_needs_update?(sass)
208
- mt = Plezi.file_mtime(sass)
209
- Plezi.get_cached(sass).each {|e| return true if File.exists?(e.options[:filename]) && (File.mtime(e.options[:filename]) > mt)} # fn = File.join( e.options[:load_paths][0].root, e.options[:filename])
210
- false
211
- end
212
- end
213
-
214
- end