kiss 1.1 → 1.7
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.
- data/LICENSE +1 -1
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/bin/kiss +151 -34
- data/data/scaffold.tgz +0 -0
- data/lib/kiss.rb +389 -742
- data/lib/kiss/accessors/controller.rb +47 -0
- data/lib/kiss/accessors/request.rb +106 -0
- data/lib/kiss/accessors/template.rb +23 -0
- data/lib/kiss/action.rb +502 -132
- data/lib/kiss/bench.rb +14 -5
- data/lib/kiss/debug.rb +14 -6
- data/lib/kiss/exception_report.rb +22 -299
- data/lib/kiss/ext/core.rb +700 -0
- data/lib/kiss/ext/rack.rb +33 -0
- data/lib/kiss/ext/sequel_database.rb +47 -0
- data/lib/kiss/ext/sequel_mysql_dataset.rb +23 -0
- data/lib/kiss/form.rb +404 -179
- data/lib/kiss/form/field.rb +183 -307
- data/lib/kiss/form/field_types.rb +239 -0
- data/lib/kiss/format.rb +88 -70
- data/lib/kiss/html/exception_report.css +222 -0
- data/lib/kiss/html/exception_report.html +210 -0
- data/lib/kiss/iterator.rb +14 -12
- data/lib/kiss/login.rb +8 -8
- data/lib/kiss/mailer.rb +68 -66
- data/lib/kiss/model.rb +323 -36
- data/lib/kiss/rack/bench.rb +16 -8
- data/lib/kiss/rack/email_errors.rb +25 -15
- data/lib/kiss/rack/errors_ok.rb +2 -2
- data/lib/kiss/rack/facebook.rb +6 -6
- data/lib/kiss/rack/file_not_found.rb +10 -8
- data/lib/kiss/rack/log_exceptions.rb +3 -3
- data/lib/kiss/rack/recorder.rb +2 -2
- data/lib/kiss/rack/show_debug.rb +2 -2
- data/lib/kiss/rack/show_exceptions.rb +2 -2
- data/lib/kiss/request.rb +435 -0
- data/lib/kiss/sequel_session.rb +15 -14
- data/lib/kiss/static_file.rb +20 -13
- data/lib/kiss/template.rb +327 -0
- metadata +60 -25
- data/lib/kiss/controller_accessors.rb +0 -81
- data/lib/kiss/hacks.rb +0 -188
- data/lib/kiss/sequel_mysql.rb +0 -25
- data/lib/kiss/template_methods.rb +0 -167
data/lib/kiss/sequel_session.rb
CHANGED
@@ -12,7 +12,7 @@ class Kiss
|
|
12
12
|
# timestamp :updated_at
|
13
13
|
# end
|
14
14
|
|
15
|
-
|
15
|
+
_attr_accessor :needs_new_cookie, :object
|
16
16
|
|
17
17
|
class << self
|
18
18
|
def setup_storage(controller)
|
@@ -21,9 +21,10 @@ class Kiss
|
|
21
21
|
primary_key :id, :null => false
|
22
22
|
column :session_id, :varchar, :null => false
|
23
23
|
column :data, :text
|
24
|
-
column :created_at, :timestamp, :null => false
|
25
24
|
column :updated_at, :timestamp, :null => false
|
26
|
-
|
25
|
+
column :created_at, :timestamp, :null => false
|
26
|
+
index :session_id, :unique => true
|
27
|
+
index :updated_at
|
27
28
|
end
|
28
29
|
|
29
30
|
# db << <<-EOT
|
@@ -93,24 +94,24 @@ class Kiss
|
|
93
94
|
end
|
94
95
|
|
95
96
|
def initialize(object)
|
96
|
-
@
|
97
|
-
@
|
97
|
+
@_object = object
|
98
|
+
@_data = self.class.unmarshal(@_object[:data]) || {}
|
98
99
|
end
|
99
100
|
|
100
101
|
def values(*args)
|
101
|
-
@
|
102
|
+
@_object.values(*args)
|
102
103
|
end
|
103
104
|
|
104
105
|
# Regenerate the Session ID
|
105
106
|
def regenerate
|
106
|
-
@
|
107
|
-
@
|
107
|
+
@_object.update_attributes(:session_id => Kiss::SessionMixin::rand_uuid)
|
108
|
+
@_needs_new_cookie = true
|
108
109
|
end
|
109
110
|
|
110
111
|
# Recreates the cookie with the default expiration time
|
111
112
|
# Useful during log in for pushing back the expiration date
|
112
113
|
def refresh_expiration
|
113
|
-
@
|
114
|
+
@_needs_new_cookie = true
|
114
115
|
end
|
115
116
|
|
116
117
|
# good
|
@@ -142,20 +143,20 @@ class Kiss
|
|
142
143
|
# good
|
143
144
|
# Lazy-unmarshal session state.
|
144
145
|
def data
|
145
|
-
@
|
146
|
+
@_data
|
146
147
|
end
|
147
148
|
|
148
149
|
# good
|
149
150
|
# Has the session been loaded yet?
|
150
151
|
def loaded?
|
151
|
-
!! @
|
152
|
+
!! @_data
|
152
153
|
end
|
153
154
|
|
154
155
|
# good
|
155
156
|
def save
|
156
|
-
@
|
157
|
-
@
|
158
|
-
@
|
157
|
+
@_object[:data] = self.class.marshal(self.data)
|
158
|
+
@_object[:updated_at] = Time.now
|
159
|
+
@_object.save
|
159
160
|
end
|
160
161
|
|
161
162
|
private
|
data/lib/kiss/static_file.rb
CHANGED
@@ -1,27 +1,34 @@
|
|
1
1
|
class Kiss
|
2
2
|
class StaticFile
|
3
|
-
def initialize(path,
|
4
|
-
@
|
5
|
-
@
|
3
|
+
def initialize(path, options = {})
|
4
|
+
@_path = path
|
5
|
+
@_options = options
|
6
6
|
end
|
7
7
|
|
8
8
|
def finish
|
9
|
-
ext = File.extname(@
|
9
|
+
ext = File.extname(@_path)[1..-1]
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
headers = {
|
12
|
+
"Last-Modified" => File.mtime(@_path).rfc822,
|
13
|
+
"Content-Type" => @_options[:type] || Kiss.mime_type(ext) || "text/plain",
|
14
|
+
"Content-Length" => File.size(@_path).to_s
|
15
|
+
}
|
16
|
+
|
17
|
+
if @_options[:filename]
|
18
|
+
headers['Content-Disposition'] = "#{@_options[:disposition] || 'inline'}; filename=#{@_options[:filename]}"
|
19
|
+
elsif @_options[:disposition]
|
20
|
+
headers['Content-Disposition'] = @_options[:disposition]
|
21
|
+
end
|
22
|
+
|
23
|
+
if File.file?(@_path) && File.readable?(@_path)
|
24
|
+
[200, headers, self]
|
17
25
|
else
|
18
|
-
|
19
|
-
["File not found: #{@path}\n"]]
|
26
|
+
raise "file not found: #{@_path}"
|
20
27
|
end
|
21
28
|
end
|
22
29
|
|
23
30
|
def each
|
24
|
-
File.open(@
|
31
|
+
File.open(@_path, "rb") { |file|
|
25
32
|
while part = file.read(8192)
|
26
33
|
yield part
|
27
34
|
end
|
@@ -0,0 +1,327 @@
|
|
1
|
+
class Kiss
|
2
|
+
class Template
|
3
|
+
class << self
|
4
|
+
_attr_reader :subdir, :filename, :path, :parent_class, :root_class, :template_dir
|
5
|
+
attr_reader :layout
|
6
|
+
|
7
|
+
# Called when this class or one of its subclasses is inherited.
|
8
|
+
# subclass (Class) - the new, inheriting subclass
|
9
|
+
def inherited(subclass)
|
10
|
+
subclass.init_child_classes
|
11
|
+
end
|
12
|
+
|
13
|
+
# Initialize the subclasses instance variable of this class (or subclass)
|
14
|
+
def init_child_classes
|
15
|
+
@_child_classes = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Gets path to template file according to specified options
|
19
|
+
# Options:
|
20
|
+
# template - template path, relative to current Template subclass subdirectory or,
|
21
|
+
# if starting with '/', relative to root template directory
|
22
|
+
# extension - template file extension, to be appended to template path unless
|
23
|
+
# template path already contains a dot
|
24
|
+
def get_template_path(options = {})
|
25
|
+
path = options[:template]
|
26
|
+
unless path =~ /\./
|
27
|
+
path += '.' + (options[:extension] || 'rhtml')
|
28
|
+
end
|
29
|
+
|
30
|
+
# If template doesn't start with '/', prepend current Template subclass
|
31
|
+
# subdirectory path.
|
32
|
+
(path =~ /\A\//) ? path : "#{options[:subdir] || self.subdir}/#{path}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_root_class(controller, root_dir)
|
36
|
+
parent_class = self
|
37
|
+
|
38
|
+
klass = Class.new(self)
|
39
|
+
Kiss.register_class_path(klass, root_dir)
|
40
|
+
klass.class_eval do
|
41
|
+
@@controller = controller
|
42
|
+
@_parent_class = parent_class
|
43
|
+
@_root_class = klass
|
44
|
+
@_template_dir = root_dir
|
45
|
+
@layout = nil
|
46
|
+
end
|
47
|
+
klass
|
48
|
+
end
|
49
|
+
|
50
|
+
# Select or create the matching template subclass for a given template path.
|
51
|
+
def get_subclass_from_path(path, default_extension = nil)
|
52
|
+
# TODO: If Kiss configured not to modify template cache,
|
53
|
+
# then we should check whether this path has a registered
|
54
|
+
# class and, if so, return it.
|
55
|
+
|
56
|
+
extension = (path =~ /\.(\w+)\Z/) ? $1 : default_extension
|
57
|
+
dot_extension = extension ? ('.' + extension) : ''
|
58
|
+
|
59
|
+
# If path starts with slash, get subclass from root template class for
|
60
|
+
# the same dot_extension. Else get subclass from this template class.
|
61
|
+
klass = if path[0,1] == '/'
|
62
|
+
path = path[1, path.length]
|
63
|
+
root_class.get_child_class(dot_extension)
|
64
|
+
else
|
65
|
+
self
|
66
|
+
end
|
67
|
+
klass.get_subclass_from_path_parts(path.split(/\/+/), dot_extension)
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_subclass_from_path_parts(parts, dot_extension = '')
|
71
|
+
part = parts.shift
|
72
|
+
return self if !part
|
73
|
+
raise 'bad template path' if part !~ /\A[\w\-\.]*\Z/i
|
74
|
+
|
75
|
+
test_path = subdir + '/' + part
|
76
|
+
klass = if @@controller.directory_exists?(@_template_dir + test_path)
|
77
|
+
get_child_class(dot_extension, test_path).
|
78
|
+
get_subclass_from_path_parts(parts, dot_extension)
|
79
|
+
else
|
80
|
+
raise 'bad template path' unless parts.empty?
|
81
|
+
get_child_class(dot_extension, subdir, part)
|
82
|
+
end
|
83
|
+
|
84
|
+
klass
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_child_class(dot_extension, subdir = '', filename = nil)
|
88
|
+
# each subdir must get a subclass of the parent dir class;
|
89
|
+
# each subclass has its own @_child_classes hash of its subdirs
|
90
|
+
|
91
|
+
is_directory_subclass = !filename
|
92
|
+
|
93
|
+
if filename
|
94
|
+
filename += dot_extension unless filename =~ /#{dot_extension}\Z/
|
95
|
+
else
|
96
|
+
filename = '_macros' + dot_extension
|
97
|
+
end
|
98
|
+
template_path = subdir + '/' + filename
|
99
|
+
absolute_fs_path = @_template_dir + template_path
|
100
|
+
template_body, template_cache_changed = @@controller.file_cache(absolute_fs_path, true)
|
101
|
+
|
102
|
+
layout_path = subdir + '/_layout' + dot_extension
|
103
|
+
layout_file, layout_cache_changed = @@controller.file_cache(@_template_dir + layout_path, true)
|
104
|
+
|
105
|
+
# Inherit this class's layout path if no layout file in subdir.
|
106
|
+
layout_path = @layout unless layout_file
|
107
|
+
|
108
|
+
if template_cache_changed || !@_child_classes.has_key?(filename)
|
109
|
+
child_class = create_child_class(
|
110
|
+
:subdir => subdir,
|
111
|
+
:filename => filename,
|
112
|
+
:layout => layout_path,
|
113
|
+
:path => template_path
|
114
|
+
)
|
115
|
+
Kiss.register_class_path(child_class, absolute_fs_path)
|
116
|
+
if template_body
|
117
|
+
source = get_ruby_source_from_erb(template_body)
|
118
|
+
source = ('def erubis(content); ' + source + '; end') unless is_directory_subclass
|
119
|
+
child_class.class_eval(source, absolute_fs_path)
|
120
|
+
end
|
121
|
+
@_child_classes[filename] = child_class
|
122
|
+
elsif layout_cache_changed
|
123
|
+
# Layout file either added or modified; in case it was newly added,
|
124
|
+
# set the subclass's @layout (if it's not already set).
|
125
|
+
@_child_classes[filename].class_eval do
|
126
|
+
@layout ||= layout_path
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
@_child_classes[filename]
|
131
|
+
end
|
132
|
+
|
133
|
+
def create_child_class(attrs)
|
134
|
+
instance_var_attrs = {
|
135
|
+
:parent_class => self,
|
136
|
+
:root_class => @_root_class,
|
137
|
+
:template_dir => @_template_dir
|
138
|
+
}.merge(attrs)
|
139
|
+
|
140
|
+
klass = Class.new(self)
|
141
|
+
klass._instance_variables_set_from_attrs(instance_var_attrs)
|
142
|
+
klass
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_ruby_source_from_erb(erb)
|
146
|
+
source = Erubis::Eruby.new(preprocess_erb(erb)).src
|
147
|
+
source.gsub!(/\b_buf\b/, '@_buf')
|
148
|
+
source
|
149
|
+
end
|
150
|
+
|
151
|
+
# Pre-process Kiss additions in ERB code.
|
152
|
+
def preprocess_erb(erb)
|
153
|
+
# macros
|
154
|
+
result = erb.gsub(
|
155
|
+
/\<\%\s*macro\s+(\w+)/,
|
156
|
+
"<% def \\1(*args, &block); ob = swap_buf(''); _macro_\\1(*args, &block); swap_buf(ob); end; def _macro_\\1"
|
157
|
+
).gsub(
|
158
|
+
/\<\%\s*end\s+macro\s*\%\>/,
|
159
|
+
'<% end %>'
|
160
|
+
)
|
161
|
+
|
162
|
+
# may be DEPRECATED soon
|
163
|
+
# generic loop iterator, using 'iterate'
|
164
|
+
result.gsub!(/\<\%\s*iterate\s+(\w+)\s+(.*?)\s*\%\>/,
|
165
|
+
"<% \\1 = Kiss::Iterator.new; \\2; \\1.increment %>")
|
166
|
+
|
167
|
+
# DEPRECATED
|
168
|
+
# for loop iterator
|
169
|
+
result.gsub!(/\<\%\s*for\s+(\w+)\s+in\s+(.*?)\s*\%\>/,
|
170
|
+
"<% \\1_loop = Kiss::Iterator.new(\\2); for \\1 in \\1_loop.collection; \\1_loop.increment %>")
|
171
|
+
|
172
|
+
result
|
173
|
+
end
|
174
|
+
|
175
|
+
def include_macros(template_path)
|
176
|
+
if template_path[0,1] != '/'
|
177
|
+
template_path = subdir + '/' + template_path
|
178
|
+
end
|
179
|
+
template_body = @@controller.file_cache(@_template_dir + template_path)
|
180
|
+
raise 'bad template path' if !template_body
|
181
|
+
class_eval(get_ruby_source_from_erb(template_body))
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
self.init_child_classes
|
186
|
+
|
187
|
+
include Kiss::ControllerAccessors
|
188
|
+
include Kiss::RequestAccessors
|
189
|
+
include Kiss::DatabaseAccessors
|
190
|
+
include Kiss::TemplateMethods
|
191
|
+
|
192
|
+
attr_accessor :layout, :title
|
193
|
+
|
194
|
+
_attr_reader :locals, :request
|
195
|
+
alias_method :local, :locals
|
196
|
+
|
197
|
+
def initialize(request, delegate = nil, predecessor = nil, locals = {})
|
198
|
+
@_delegate = delegate
|
199
|
+
@_request = request
|
200
|
+
@_controller = delegate.controller if delegate
|
201
|
+
@_predecessor = predecessor || delegate
|
202
|
+
@_locals = locals
|
203
|
+
end
|
204
|
+
|
205
|
+
def make_layout_absolute(relative_object)
|
206
|
+
if @layout && @layout !~ /\A\//
|
207
|
+
@layout = relative_object.class.subdir + '/' + @layout
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def call(content = nil)
|
212
|
+
import_instance_variables(@_predecessor)
|
213
|
+
make_layout_absolute(@_predecessor)
|
214
|
+
|
215
|
+
content = erubis(content)
|
216
|
+
|
217
|
+
# self.class.no_more_macros
|
218
|
+
|
219
|
+
make_layout_absolute(self)
|
220
|
+
export_instance_variables(@_predecessor)
|
221
|
+
|
222
|
+
content
|
223
|
+
end
|
224
|
+
|
225
|
+
def erubis(content, locals = {})
|
226
|
+
raise Kiss::FileNotFoundError::Template, "template not found: '#{self.class.path}'"
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
def include_macros(template_path)
|
232
|
+
self.class.include_macros(template_path)
|
233
|
+
end
|
234
|
+
|
235
|
+
def method_missing(method, *args, &block)
|
236
|
+
if method.to_s =~ /\A_class_(\d+)_(.*)/
|
237
|
+
__send__ $2.to_sym, $1.to_i, *args, &block
|
238
|
+
else
|
239
|
+
@_delegate.__send__ method, *args, &block
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def context_class
|
244
|
+
Kiss.context_class
|
245
|
+
end
|
246
|
+
|
247
|
+
# Reads file specified by options and return contents (without template
|
248
|
+
# processing).
|
249
|
+
def read_file(filename)
|
250
|
+
File.read(filename)
|
251
|
+
end
|
252
|
+
|
253
|
+
def insert(*args)
|
254
|
+
File.read(self.class.template_dir + context_class.get_template_path(get_options_hash(*args)))
|
255
|
+
end
|
256
|
+
alias_method :insert_file, :insert
|
257
|
+
|
258
|
+
# Processes template specified by options and return results.
|
259
|
+
def process_template(*args)
|
260
|
+
options = get_options_hash(*args)
|
261
|
+
path = context_class.get_template_path(options)
|
262
|
+
|
263
|
+
if (options[:data] || options[:vars])
|
264
|
+
new_data = (options[:data] || {}).merge(options[:vars] || {})
|
265
|
+
old_data = self.data
|
266
|
+
self.data = self.data.merge(new_data)
|
267
|
+
else
|
268
|
+
old_data = nil
|
269
|
+
end
|
270
|
+
|
271
|
+
# can Template.get_child_class do the path parsing? maybe...
|
272
|
+
# and then Action.get_child_class can do the same? what about parsing mixed-in args?
|
273
|
+
content = self.class.root_class.get_subclass_from_path(path).new(request, @_delegate, self, options[:locals] || options[:local] || {}).call
|
274
|
+
self.data = old_data if old_data
|
275
|
+
|
276
|
+
content
|
277
|
+
end
|
278
|
+
alias_method :render, :process_template
|
279
|
+
alias_method :process, :process_template
|
280
|
+
alias_method :process_partial, :process_template
|
281
|
+
alias_method :render_partial, :process_template
|
282
|
+
|
283
|
+
# Convert arguments into options hash for use get_template_path.
|
284
|
+
# - get_options_hash(template_path)
|
285
|
+
# - get_options_hash(template_path, options_hash)
|
286
|
+
# - get_options_hash(options_hash)
|
287
|
+
# where template_path is relative to the app's template_dir
|
288
|
+
# and options_hash specifies:
|
289
|
+
# - template: template path
|
290
|
+
# - layout: layout template path
|
291
|
+
def get_options_hash(*args)
|
292
|
+
case args.length
|
293
|
+
when 0
|
294
|
+
{}
|
295
|
+
when 1
|
296
|
+
args[0].is_a?(Hash) ? args[0] : { :template => args[0] }
|
297
|
+
when 2
|
298
|
+
args[1].merge( :template => args[0] )
|
299
|
+
else
|
300
|
+
raise 'too many arguments'
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
# Escapes string for use in URLs.
|
306
|
+
def url_escape(string)
|
307
|
+
string.url_escape
|
308
|
+
end
|
309
|
+
alias_method :u, :url_escape
|
310
|
+
alias_method :escape, :url_escape
|
311
|
+
alias_method :url_encode, :url_escape
|
312
|
+
alias_method :escape_url, :url_escape
|
313
|
+
|
314
|
+
# Encodes string for output to HTML.
|
315
|
+
def html_escape(str)
|
316
|
+
str.html_escape
|
317
|
+
end
|
318
|
+
alias_method :h, :html_escape
|
319
|
+
alias_method :escape_html, :html_escape
|
320
|
+
|
321
|
+
def swap_buf(n)
|
322
|
+
o = @_buf
|
323
|
+
@_buf = n
|
324
|
+
o
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|