plezi 0.9.2 → 0.10.1

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