garterbelt 0.0.2

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 (57) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +16 -0
  4. data/Gemfile.lock +38 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +21 -0
  7. data/Rakefile +46 -0
  8. data/TODO +3 -0
  9. data/VERSION +1 -0
  10. data/garterbelt.gemspec +165 -0
  11. data/lib/garterbelt.rb +23 -0
  12. data/lib/page.rb +46 -0
  13. data/lib/renderers/cache.rb +35 -0
  14. data/lib/renderers/closed_tag.rb +60 -0
  15. data/lib/renderers/comment.rb +14 -0
  16. data/lib/renderers/content_rendering.rb +41 -0
  17. data/lib/renderers/content_tag.rb +36 -0
  18. data/lib/renderers/doctype.rb +24 -0
  19. data/lib/renderers/renderer.rb +33 -0
  20. data/lib/renderers/text.rb +28 -0
  21. data/lib/renderers/xml.rb +9 -0
  22. data/lib/stocking.rb +11 -0
  23. data/lib/support/string.rb +165 -0
  24. data/lib/view.rb +341 -0
  25. data/spec/benchmark/templates/erector.rb +37 -0
  26. data/spec/benchmark/templates/garterbelt.rb +37 -0
  27. data/spec/benchmark/vs_erector.rb +53 -0
  28. data/spec/garterbelt_spec.rb +49 -0
  29. data/spec/integration/expectations/general_view.html +17 -0
  30. data/spec/integration/expectations/variables/view_with_user_and_params.html +23 -0
  31. data/spec/integration/expectations/variables/view_with_user_email.html +23 -0
  32. data/spec/integration/expectations/view_partial_nest.html +24 -0
  33. data/spec/integration/expectations/view_with_tags.html +19 -0
  34. data/spec/integration/templates/view_partial_nest.rb +22 -0
  35. data/spec/integration/templates/view_with_cache.rb +30 -0
  36. data/spec/integration/templates/view_with_partial.rb +36 -0
  37. data/spec/integration/templates/view_with_partial_2.rb +36 -0
  38. data/spec/integration/templates/view_with_tags.rb +26 -0
  39. data/spec/integration/templates/view_with_vars.rb +32 -0
  40. data/spec/integration/view_spec.rb +57 -0
  41. data/spec/page_spec.rb +99 -0
  42. data/spec/renderers/cache_spec.rb +85 -0
  43. data/spec/renderers/closed_tag_spec.rb +172 -0
  44. data/spec/renderers/comment_spec.rb +68 -0
  45. data/spec/renderers/content_tag_spec.rb +150 -0
  46. data/spec/renderers/doctype_spec.rb +46 -0
  47. data/spec/renderers/text_spec.rb +68 -0
  48. data/spec/spec_helper.rb +17 -0
  49. data/spec/support/mock_view.rb +14 -0
  50. data/spec/support/puters.rb +10 -0
  51. data/spec/view/view_basics_spec.rb +106 -0
  52. data/spec/view/view_caching_spec.rb +132 -0
  53. data/spec/view/view_partial_spec.rb +63 -0
  54. data/spec/view/view_rails_type_helpers.rb +148 -0
  55. data/spec/view/view_render_spec.rb +408 -0
  56. data/spec/view/view_variables_spec.rb +159 -0
  57. metadata +367 -0
@@ -0,0 +1,9 @@
1
+ module Garterbelt
2
+ class Xml < ClosedTag
3
+ max_pool_size 1000
4
+
5
+ def template
6
+ "#{indent}<?xml #{rendered_attributes} ?>\n"
7
+ end
8
+ end
9
+ end
data/lib/stocking.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Garterbelt
2
+ def self.cache_hash
3
+ @cache_hash ||= {:default => Moneta::Memory.new}
4
+ end
5
+
6
+ def self.cache(store = :default)
7
+ c = cache_hash[store]
8
+ raise "Cache #{store.inspect} has not yet been configured" unless c
9
+ c
10
+ end
11
+ end
@@ -0,0 +1,165 @@
1
+ require 'active_support/inflector/methods'
2
+ require 'active_support/inflector/inflections'
3
+
4
+ # including the string extension directly require i18l gem, which is lame when not needed
5
+ # String inflections define new methods on the String class to transform names for different purposes.
6
+ # For instance, you can figure out the name of a database from the name of a class.
7
+ #
8
+ # "ScaleScore".tableize # => "scale_scores"
9
+ #
10
+
11
+ class String
12
+ # Returns the plural form of the word in the string.
13
+ #
14
+ # "post".pluralize # => "posts"
15
+ # "octopus".pluralize # => "octopi"
16
+ # "sheep".pluralize # => "sheep"
17
+ # "words".pluralize # => "words"
18
+ # "the blue mailman".pluralize # => "the blue mailmen"
19
+ # "CamelOctopus".pluralize # => "CamelOctopi"
20
+ def pluralize
21
+ ActiveSupport::Inflector.pluralize(self)
22
+ end
23
+
24
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
25
+ #
26
+ # "posts".singularize # => "post"
27
+ # "octopi".singularize # => "octopus"
28
+ # "sheep".singularize # => "sheep"
29
+ # "word".singularize # => "word"
30
+ # "the blue mailmen".singularize # => "the blue mailman"
31
+ # "CamelOctopi".singularize # => "CamelOctopus"
32
+ def singularize
33
+ ActiveSupport::Inflector.singularize(self)
34
+ end
35
+
36
+ # +constantize+ tries to find a declared constant with the name specified
37
+ # in the string. It raises a NameError when the name is not in CamelCase
38
+ # or is not initialized.
39
+ #
40
+ # Examples
41
+ # "Module".constantize # => Module
42
+ # "Class".constantize # => Class
43
+ def constantize
44
+ ActiveSupport::Inflector.constantize(self)
45
+ end
46
+
47
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
48
+ # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
49
+ #
50
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
51
+ #
52
+ # "active_record".camelize # => "ActiveRecord"
53
+ # "active_record".camelize(:lower) # => "activeRecord"
54
+ # "active_record/errors".camelize # => "ActiveRecord::Errors"
55
+ # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
56
+ def camelize(first_letter = :upper)
57
+ case first_letter
58
+ when :upper then ActiveSupport::Inflector.camelize(self, true)
59
+ when :lower then ActiveSupport::Inflector.camelize(self, false)
60
+ end
61
+ end
62
+ alias_method :camelcase, :camelize
63
+
64
+ # Capitalizes all the words and replaces some characters in the string to create
65
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
66
+ # used in the Rails internals.
67
+ #
68
+ # +titleize+ is also aliased as +titlecase+.
69
+ #
70
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
71
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
72
+ def titleize
73
+ ActiveSupport::Inflector.titleize(self)
74
+ end
75
+ alias_method :titlecase, :titleize
76
+
77
+ # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
78
+ #
79
+ # +underscore+ will also change '::' to '/' to convert namespaces to paths.
80
+ #
81
+ # "ActiveRecord".underscore # => "active_record"
82
+ # "ActiveRecord::Errors".underscore # => active_record/errors
83
+ def underscore
84
+ ActiveSupport::Inflector.underscore(self)
85
+ end
86
+
87
+ # Replaces underscores with dashes in the string.
88
+ #
89
+ # "puni_puni" # => "puni-puni"
90
+ def dasherize
91
+ ActiveSupport::Inflector.dasherize(self)
92
+ end
93
+
94
+ # Removes the module part from the constant expression in the string.
95
+ #
96
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
97
+ # "Inflections".demodulize # => "Inflections"
98
+ def demodulize
99
+ ActiveSupport::Inflector.demodulize(self)
100
+ end
101
+
102
+ # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
103
+ #
104
+ # ==== Examples
105
+ #
106
+ # class Person
107
+ # def to_param
108
+ # "#{id}-#{name.parameterize}"
109
+ # end
110
+ # end
111
+ #
112
+ # @person = Person.find(1)
113
+ # # => #<Person id: 1, name: "Donald E. Knuth">
114
+ #
115
+ # <%= link_to(@person.name, person_path %>
116
+ # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
117
+ def parameterize(sep = '-')
118
+ ActiveSupport::Inflector.parameterize(self, sep)
119
+ end
120
+
121
+ # Creates the name of a table like Rails does for models to table names. This method
122
+ # uses the +pluralize+ method on the last word in the string.
123
+ #
124
+ # "RawScaledScorer".tableize # => "raw_scaled_scorers"
125
+ # "egg_and_ham".tableize # => "egg_and_hams"
126
+ # "fancyCategory".tableize # => "fancy_categories"
127
+ def tableize
128
+ ActiveSupport::Inflector.tableize(self)
129
+ end
130
+
131
+ # Create a class name from a plural table name like Rails does for table names to models.
132
+ # Note that this returns a string and not a class. (To convert to an actual class
133
+ # follow +classify+ with +constantize+.)
134
+ #
135
+ # "egg_and_hams".classify # => "EggAndHam"
136
+ # "posts".classify # => "Post"
137
+ #
138
+ # Singular names are not handled correctly.
139
+ #
140
+ # "business".classify # => "Busines"
141
+ def classify
142
+ ActiveSupport::Inflector.classify(self)
143
+ end
144
+
145
+ # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
146
+ # Like +titleize+, this is meant for creating pretty output.
147
+ #
148
+ # "employee_salary" # => "Employee salary"
149
+ # "author_id" # => "Author"
150
+ def humanize
151
+ ActiveSupport::Inflector.humanize(self)
152
+ end
153
+
154
+ # Creates a foreign key name from a class name.
155
+ # +separate_class_name_and_id_with_underscore+ sets whether
156
+ # the method should put '_' between the name and 'id'.
157
+ #
158
+ # Examples
159
+ # "Message".foreign_key # => "message_id"
160
+ # "Message".foreign_key(false) # => "messageid"
161
+ # "Admin::Post".foreign_key # => "post_id"
162
+ def foreign_key(separate_class_name_and_id_with_underscore = true)
163
+ ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
164
+ end
165
+ end
data/lib/view.rb ADDED
@@ -0,0 +1,341 @@
1
+ module Garterbelt
2
+ class View
3
+ include RuPol::Swimsuit
4
+
5
+ attr_accessor :output, :buffer, :level, :escape
6
+ attr_reader :curator
7
+
8
+ def initialize(opts={})
9
+ self.buffer = []
10
+ self.level = (opts.delete(:level) || 0)
11
+ self.output = ""
12
+ self.escape = true
13
+
14
+ self.curator = opts.delete(:curator) || self
15
+
16
+ params = self.class.default_variables.merge(opts)
17
+ keys = params.keys
18
+
19
+ unless ((self.class.required || []) - keys).empty?
20
+ raise ArgumentError, "#{(self.class.required - keys).inspect} required as an initialization option"
21
+ end
22
+
23
+ if self.class.selective_require && keys != self.class.required
24
+ raise ArgumentError, "Allowed initalization options are only #{self.class.required.inspect}"
25
+ end
26
+
27
+ params.each do |key, value|
28
+ self.class.add_accssor(key) unless respond_to?(key)
29
+ send("#{key}=", value)
30
+ end
31
+ end
32
+
33
+ def curator=(parent_view)
34
+ @curator = parent_view
35
+ if parent_view != self
36
+ self.buffer = parent_view.buffer
37
+ self.level = parent_view.level
38
+ self.output = parent_view.output
39
+ self.escape = parent_view.escape
40
+ end
41
+ end
42
+
43
+ def curated?
44
+ curator === self
45
+ end
46
+
47
+ # VARIABLE ACCESS -----------------------------
48
+ class << self
49
+ attr_accessor :required, :selective_require
50
+ end
51
+
52
+ def self.add_accssor key
53
+ key = key.to_s
54
+ return if accessories.include?(key)
55
+ if instance_methods.include?(key)
56
+ raise ArgumentError, ":#{key} cannot be a required variable because it maps to an existing method"
57
+ end
58
+
59
+ accessories << key.to_s
60
+ attr_accessor key
61
+ end
62
+
63
+ def self.accessories
64
+ @accessories ||= superclass? ? superclass.accessories.dup : []
65
+ end
66
+
67
+ def self.superclass?
68
+ @superclassed ||= superclass.respond_to?( :required )
69
+ end
70
+
71
+ def self.super_required
72
+ superclass? ? superclass.required || [] : []
73
+ end
74
+
75
+ def self.default_variables
76
+ @default_variables ||= superclass? ? superclass.default_variables.dup : Hash.new
77
+ end
78
+
79
+ def self.requires *args
80
+ if args.last.is_a?(Hash)
81
+ self.default_variables.merge!(args.pop)
82
+ args += default_variables.keys.map{ |x| x.to_sym }
83
+ end
84
+
85
+ args = super_required + args
86
+ self.required = args.uniq
87
+ build_accessors
88
+ required
89
+ end
90
+
91
+ def self.requires_only(*args)
92
+ self.selective_require = true
93
+ requires(*args)
94
+ end
95
+
96
+ class << self
97
+ alias :needs :requires
98
+ alias :needs_only :requires_only
99
+ end
100
+
101
+ def self.build_accessors
102
+ required.each do |m|
103
+ add_accssor m
104
+ end
105
+ end
106
+
107
+ # TAG HELPERS -----------------------
108
+
109
+ def add_to_buffer(renderer)
110
+ buffer << renderer
111
+ renderer
112
+ end
113
+
114
+ def tag(type, *args, &block)
115
+ add_to_buffer ContentTag.new(parse_tag_arguments(type, args), &block)
116
+ end
117
+
118
+ def closed_tag(type, *args)
119
+ add_to_buffer ClosedTag.new(parse_tag_arguments(type, args))
120
+ end
121
+
122
+ def non_escape_tag(*args, &block)
123
+ if escape
124
+ curator.escape = false
125
+ t = tag(*args, &block)
126
+ curator.escape = true
127
+ t
128
+ else
129
+ tag(*args, &block)
130
+ end
131
+ end
132
+
133
+ def text(content)
134
+ add_to_buffer Text.new(:view => curator, :content => content)
135
+ end
136
+
137
+ alias :h :text
138
+
139
+ def raw_text(content)
140
+ if escape
141
+ curator.escape = false
142
+ t = text(content)
143
+ curator.escape = true
144
+ t
145
+ else
146
+ text(content)
147
+ end
148
+ end
149
+ alias :raw :raw_text
150
+ alias :rawtext :raw_text
151
+
152
+ def comment(content)
153
+ add_to_buffer Comment.new(:view => curator, :content => content)
154
+ end
155
+
156
+ def doctype(type=:transitional)
157
+ add_to_buffer Doctype.new(:view => curator, :type => type)
158
+ end
159
+
160
+ def xml(opts={})
161
+ opts = {:version => 1.0, :encoding => 'utf-8'}.merge(opts)
162
+ add_to_buffer Xml.new(parse_tag_arguments(:xml, [opts]))
163
+ end
164
+
165
+ def parse_tag_arguments(type, args)
166
+ opts = {:type => type, :view => curator}
167
+ if args.size == 2
168
+ opts[:content] = args.shift
169
+ opts[:attributes] = args.first
170
+ else
171
+ if args.first.is_a?(Hash)
172
+ opts[:attributes] = args.first
173
+ else
174
+ opts[:content] = args.first
175
+ end
176
+ end
177
+ opts
178
+ end
179
+
180
+ CONTENT_TAGS = [
181
+ 'a', 'abbr', 'acronym', 'address',
182
+ 'b', 'bdo', 'big', 'blockquote', 'body', 'button',
183
+ 'caption', 'center', 'cite', 'colgroup',
184
+ 'dd', 'del', 'dfn', 'div', 'dl', 'dt', 'em',
185
+ 'embed',
186
+ 'fieldset', 'form', 'frameset',
187
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'i',
188
+ 'iframe', 'ins', 'kbd', 'label', 'legend', 'li', 'map',
189
+ 'noframes', 'noscript',
190
+ 'object', 'ol', 'optgroup', 'option', 'p', 'param',
191
+ 'q', 's',
192
+ 'samp', 'script', 'select', 'small', 'span',
193
+ 'strong', 'style', 'sub', 'sup',
194
+ 'table', 'tbody', 'td', 'textarea', 'tfoot',
195
+ 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var'
196
+ ]
197
+ CONTENT_TAGS.each do |type|
198
+ class_eval <<-RUBY
199
+ def #{type}(*args, &block)
200
+ tag(:#{type}, *args, &block)
201
+ end
202
+ RUBY
203
+ end
204
+
205
+ NON_ESCAPE_TAGS = ['code', 'pre']
206
+ NON_ESCAPE_TAGS.each do |type|
207
+ class_eval <<-RUBY
208
+ def #{type}(*args, &block)
209
+ non_escape_tag(:#{type}, *args, &block)
210
+ end
211
+ RUBY
212
+ end
213
+
214
+ CLOSED_TAGS = ['area', 'br', 'col', 'frame', 'hr', 'img', 'input']
215
+ CLOSED_TAGS.each do |type|
216
+ class_eval <<-RUBY
217
+ def #{type}(*args)
218
+ closed_tag(:#{type}, *args)
219
+ end
220
+ RUBY
221
+ end
222
+
223
+ HEAD_TAGS = ['base', 'meta', 'link']
224
+ HEAD_TAGS.each do |type|
225
+ class_eval <<-RUBY
226
+ def _#{type}(*args)
227
+ closed_tag(:#{type}, *args)
228
+ end
229
+ RUBY
230
+ end
231
+
232
+ def page_title(*args, &block)
233
+ tag(:title, *args, &block)
234
+ end
235
+
236
+ def stylesheet_link(path)
237
+ _link(:rel => "stylesheet", 'type' => "text/css", :href => "#{path}.css")
238
+ end
239
+
240
+ def javascript_link(path)
241
+ script(:src => "#{path}.js", 'type' => "text/javascript")
242
+ end
243
+
244
+ # RENDERING -------------------------
245
+
246
+ def content
247
+ raise NotImplementedError, "Implement #content in #{self.class}!"
248
+ end
249
+
250
+ def render(content_method = :content)
251
+ self.output = "" if curated?
252
+ if content_method == :content
253
+ content
254
+ else
255
+ send(content_method)
256
+ end
257
+ render_buffer
258
+ output
259
+ end
260
+
261
+ alias :to_s :render
262
+ alias :to_html :render
263
+
264
+ def render_buffer
265
+ array = buffer.dup
266
+ buffer.clear
267
+ array.each do |item|
268
+ if item.respond_to?(:render)
269
+ item.render
270
+ item.recycle
271
+ else
272
+ output << item.to_s
273
+ end
274
+ end
275
+ end
276
+
277
+ def self.render(opts={})
278
+ content_method = opts[:method]
279
+ view = new
280
+ output = content_method ? view.render(content_method) : view.render
281
+ view.recycle
282
+ output
283
+ end
284
+
285
+ def partial(*args, &block)
286
+ if (klass = args.first).is_a?(Class)
287
+ args.shift
288
+ view = klass.new(*args)
289
+ else
290
+ view = args.first
291
+ end
292
+ view.curator = curator
293
+ self.buffer << view
294
+ view
295
+ end
296
+
297
+ alias :widget :partial
298
+
299
+ # CACHING ---------------------------
300
+
301
+ class << self
302
+ attr_writer :cache_store_key, :cache_key_base
303
+ end
304
+
305
+ attr_writer :cache_store_key, :cache_key_base
306
+
307
+
308
+ def self.cache_store_key
309
+ @cache_store_key ||= :default
310
+ end
311
+
312
+ def cache_store_key
313
+ @cache_store_key ||= self.class.cache_store_key
314
+ end
315
+
316
+ def cache_store
317
+ @cache ||= Garterbelt.cache(cache_store_key)
318
+ end
319
+
320
+ def self.cache_key_base
321
+ @cache_key_base ||= self.to_s.underscore
322
+ end
323
+
324
+ def cache_key_base
325
+ @cache_key_base ||= self.class.cache_key_base
326
+ end
327
+
328
+ CACHE_DETAIL_DEFAULT = 'default'
329
+
330
+ def cache_key(detail = CACHE_DETAIL_DEFAULT)
331
+ detail ||= CACHE_DETAIL_DEFAULT
332
+ "#{cache_key_base}_#{detail}"
333
+ end
334
+
335
+ def cache(key, &block)
336
+ renderer = Cache.new(:view => self, :key => cache_key(key), &block)
337
+ buffer << renderer
338
+ renderer
339
+ end
340
+ end
341
+ end
@@ -0,0 +1,37 @@
1
+ class ErectorTemplate < Erector::Widget
2
+ needs :user, :flash => nil
3
+
4
+ def content
5
+ html do
6
+ head do
7
+ title "Benchmarking Templates"
8
+ link :rel => 'stylesheet', :href => '/stylesheet/application.css'
9
+ link :rel => 'stylesheet', :href => '/stylesheet/ie.css'
10
+ end
11
+
12
+ body :class => self.class.to_s.underscore do
13
+ div :id => :wrapper, :class => 'line' do
14
+ if flash
15
+ div :id => 'flash', :class => 'inner' do
16
+ text flash
17
+ end
18
+ end
19
+
20
+ dl :class => ['inner', 'user_info'] do
21
+ dt 'username'
22
+ dd user.username
23
+
24
+ dt 'email'
25
+ dd user.email
26
+
27
+ dt 'name'
28
+ dd user.name
29
+
30
+ dt 'number of assigned tasks'
31
+ dd rand(20)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ class GarterbeltTemplate < Garterbelt::View
2
+ needs :user, :flash => nil
3
+
4
+ def content
5
+ html do
6
+ head do
7
+ title "Benchmarking Templates"
8
+ link :rel => 'stylesheet', :href => '/stylesheet/application.css'
9
+ link :rel => 'stylesheet', :href => '/stylesheet/ie.css'
10
+ end
11
+
12
+ body :class => self.class.to_s.underscore do
13
+ div :id => :wrapper, :class => 'line' do
14
+ if flash
15
+ div :id => 'flash', :class => 'inner' do
16
+ text flash
17
+ end
18
+ end
19
+
20
+ dl :class => ['inner', 'user_info'] do
21
+ dt 'username'
22
+ dd user.username
23
+
24
+ dt 'email'
25
+ dd user.email
26
+
27
+ dt 'name'
28
+ dd user.name
29
+
30
+ dt 'number of assigned tasks'
31
+ dd rand(20)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require "rbench"
5
+ require 'hashie'
6
+
7
+ require 'erector'
8
+ require File.dirname(__FILE__) + "/../../lib/garterbelt"
9
+
10
+ require File.dirname(__FILE__) + '/templates/stocking'
11
+ require File.dirname(__FILE__) + '/templates/erector'
12
+
13
+ TIMES = 500_000
14
+
15
+ RBench.run(TIMES) do
16
+ LoungeTemplate.max_pool_size TIMES/10
17
+ column :garterbelt
18
+ column :erector
19
+
20
+ report "Simple Page Rendering" do
21
+ user = Hashie::Mash.new(:username => 'baccigalupi', :email => 'baccigalupi@example.com', :name => 'Kane Baccigalupi')
22
+ erector { ErectorTemplate.new(:user => user) }
23
+ garterbelt { GarterbeltTemplate.new(:user => user) }
24
+ end
25
+ end
26
+
27
+ # 4/11/2011, version 0.0.1, standard pooling
28
+ # GARTERBELT = pooling at standard 1000 instances
29
+ # GARTERBELT_2 = pooling at 10% of sample time
30
+
31
+ # 10_000 times
32
+ # GARTERBELT | ERECTOR |
33
+ # ---------------------------------------------------------------------
34
+ # Simple Page Rendering 0.164 | 0.205 | 20% faster
35
+
36
+ # 100_000 times
37
+ # GARTERBELT | GARTERBELT_2 | ERECTOR |
38
+ # ---------------------------------------------------------------------
39
+ # Simple Page Rendering 1.857 | 1.828 | 1.932 | 3.8-5.3%/ faster
40
+
41
+ # 200_000 times
42
+ # GARTERBELT | GARTERBELT_2 | ERECTOR |
43
+ # ---------------------------------------------------------------------
44
+ # Simple Page Rendering 3.743 | 3.660 | 3.846 | 2.7-4.8% faster
45
+
46
+ # 500_000 times
47
+ # GARTERBELT | GARTERBELT_2 | ERECTOR |
48
+ # ---------------------------------------------------------------------
49
+ # Simple Page Rendering 9.420 | 9.422 | 9.637 | 2.3-2.2% faster
50
+
51
+
52
+
53
+
@@ -0,0 +1,49 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Garterbelt do
4
+ describe 'cache module level methods' do
5
+ describe '#cache_hash' do
6
+ it 'creates a default hash when accessed the first time' do
7
+ cache_hash = Garterbelt.cache_hash.dup
8
+ cache_hash.class.should == Hash
9
+ cache_hash[:default].is_a?(Moneta::Memory).should == true
10
+ cache_hash.keys.size.should == 1
11
+ end
12
+
13
+ it 'gives direct access to the hash' do
14
+ Garterbelt.cache_hash[:foo] = 'foo'
15
+ Garterbelt.cache_hash[:foo].should == 'foo'
16
+ end
17
+ end
18
+
19
+ describe '#cache=' do
20
+ it 'creates a Moneta cache' do
21
+
22
+ end
23
+
24
+ describe 'options' do
25
+ it 'creates a memory Moneta cache correctly'
26
+ it 'creates a file system Moneta cache'
27
+ it 'creates alternative types as requested'
28
+ end
29
+ it 'sets the default key when not provided a argument'
30
+ it 'sets other keys when not '
31
+ end
32
+
33
+ describe '#cache' do
34
+ it 'returns the :default if no arugment is received' do
35
+ Garterbelt.cache.should == Garterbelt.cache_hash[:default]
36
+ end
37
+
38
+ it 'returns an alternative cache by key' do
39
+ Garterbelt.cache_hash[:foo] = 'foo'
40
+ Garterbelt.cache(:foo).should == 'foo'
41
+ end
42
+
43
+ it 'raises an error when accessing via a key that has no been configured' do
44
+ Garterbelt.cache_hash[:bar].should be_nil
45
+ lambda { Garterbelt.cache(:bar) }.should raise_error( "Cache :bar has not yet been configured" )
46
+ end
47
+ end
48
+ end
49
+ end