mustache_render 0.0.1 → 0.0.3

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 (32) hide show
  1. data/MIT-LICENSE +1 -1
  2. data/README.rdoc +20 -1
  3. data/lib/generators/mustache_render/migration/install_generator.rb +53 -0
  4. data/lib/generators/mustache_render/migration/templates/active_record/migration.rb +37 -0
  5. data/lib/generators/mustache_render/migration/templates/models/active_record/mustache_render_folder.rb +6 -0
  6. data/lib/generators/mustache_render/migration/templates/models/active_record/mustache_render_template.rb +6 -0
  7. data/lib/mustache_render.rb +26 -0
  8. data/lib/mustache_render/config.rb +22 -0
  9. data/lib/mustache_render/controllers/mustache_render/manager/base_controller.rb +16 -0
  10. data/lib/mustache_render/controllers/mustache_render/manager/folders_controller.rb +46 -0
  11. data/lib/mustache_render/controllers/mustache_render/manager/templates_controller.rb +53 -0
  12. data/lib/mustache_render/core_ext/base_controller_ext.rb +41 -0
  13. data/lib/mustache_render/models/mustache_render_folder_mixin.rb +128 -0
  14. data/lib/mustache_render/models/mustache_render_template_mixin.rb +44 -0
  15. data/lib/mustache_render/mustache.rb +314 -0
  16. data/lib/mustache_render/mustache/context.rb +144 -0
  17. data/lib/mustache_render/mustache/generator.rb +197 -0
  18. data/lib/mustache_render/mustache/parser.rb +265 -0
  19. data/lib/mustache_render/mustache/settings.rb +234 -0
  20. data/lib/mustache_render/mustache/template.rb +60 -0
  21. data/lib/mustache_render/version.rb +8 -1
  22. data/lib/mustache_render/views/layouts/mustache_render/manager/base.html.erb +14 -0
  23. data/lib/mustache_render/views/mustache_render/manager/folders/_form.html.erb +31 -0
  24. data/lib/mustache_render/views/mustache_render/manager/folders/edit.html.erb +11 -0
  25. data/lib/mustache_render/views/mustache_render/manager/folders/index.html.erb +9 -0
  26. data/lib/mustache_render/views/mustache_render/manager/folders/new.html.erb +8 -0
  27. data/lib/mustache_render/views/mustache_render/manager/folders/show.html.erb +44 -0
  28. data/lib/mustache_render/views/mustache_render/manager/templates/_form.html.erb +36 -0
  29. data/lib/mustache_render/views/mustache_render/manager/templates/edit.html.erb +15 -0
  30. data/lib/mustache_render/views/mustache_render/manager/templates/new.html.erb +12 -0
  31. data/lib/mustache_render/views/mustache_render/manager/templates/show.html.erb +33 -0
  32. metadata +106 -30
@@ -0,0 +1,44 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module MustacheRender::Models
3
+ module MustacheRenderTemplateMixin
4
+ def self.included(base)
5
+ base.class_eval do
6
+ table_name = 'mustache_render_templates'
7
+
8
+ attr_accessible :folder_id, :name, :note, :content
9
+
10
+ belongs_to :folder, :class_name => 'MustacheRenderFolder'
11
+
12
+ validates_presence_of :folder_id
13
+ validates_presence_of :name
14
+
15
+ extend ClassMethods
16
+ include InstanceMethods
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ #
22
+ # TODO: add cache here!
23
+ #
24
+ def find_with_full_path(name)
25
+ # 首先获取文件夹的名称, 然后获取文件名
26
+ tmp_paths = name.to_s.split('/')
27
+
28
+ template_name = tmp_paths.pop.to_s
29
+
30
+ folder_full_path = "#{tmp_paths.join('/')}"
31
+ folder = ::MustacheRenderFolder.find_by_full_path(folder_full_path)
32
+
33
+ self.find_by_folder_id_and_name(folder.try(:id), template_name)
34
+ end
35
+ end
36
+
37
+ module InstanceMethods
38
+ def full_path
39
+ "#{self.folder.try :full_path}/#{self.name}"
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,314 @@
1
+ require 'mustache_render/mustache/template'
2
+ require 'mustache_render/mustache/context'
3
+ require 'mustache_render/mustache/settings'
4
+
5
+ # Mustache is the base class from which your Mustache subclasses
6
+ # should inherit (though it can be used on its own).
7
+ #
8
+ # The typical Mustache workflow is as follows:
9
+ #
10
+ # * Create a Mustache subclass: class Stats < Mustache
11
+ # * Create a template: stats.mustache
12
+ # * Instantiate an instance: view = Stats.new
13
+ # * Render that instance: view.render
14
+ #
15
+ # You can skip the instantiation by calling `Stats.render` directly.
16
+ #
17
+ # While Mustache will do its best to load and render a template for
18
+ # you, this process is completely customizable using a few options.
19
+ #
20
+ # All settings can be overriden at the class level.
21
+ #
22
+ # For example, going with the above example, we can use
23
+ # `Stats.template_path = "/usr/local/templates"` to specify the path
24
+ # Mustache uses to find templates.
25
+ #
26
+ # Here are the available options:
27
+ #
28
+ # * template_path
29
+ #
30
+ # The `template_path` setting determines the path Mustache uses when
31
+ # looking for a template. By default it is "."
32
+ # Setting it to /usr/local/templates, for example, means (given all
33
+ # other settings are default) a Mustache subclass `Stats` will try to
34
+ # load /usr/local/templates/stats.mustache
35
+ #
36
+ # * template_extension
37
+ #
38
+ # The `template_extension` is the extension Mustache uses when looking
39
+ # for template files. By default it is "mustache"
40
+ #
41
+ # * template_file
42
+ #
43
+ # You can tell Mustache exactly which template to us with this
44
+ # setting. It can be a relative or absolute path.
45
+ #
46
+ # * template
47
+ #
48
+ # Sometimes you want Mustache to render a string, not a file. In those
49
+ # cases you may set the `template` setting. For example:
50
+ #
51
+ # >> Mustache.render("Hello {{planet}}", :planet => "World!")
52
+ # => "Hello World!"
53
+ #
54
+ # The `template` setting is also available on instances.
55
+ #
56
+ # view = Mustache.new
57
+ # view.template = "Hi, {{person}}!"
58
+ # view[:person] = 'Mom'
59
+ # view.render # => Hi, mom!
60
+ #
61
+ # * view_namespace
62
+ #
63
+ # To make life easy on those developing Mustache plugins for web frameworks or
64
+ # other libraries, Mustache will attempt to load view classes (i.e. Mustache
65
+ # subclasses) using the `view_class` class method. The `view_namespace` tells
66
+ # Mustache under which constant view classes live. By default it is `Object`.
67
+ #
68
+ # * view_path
69
+ #
70
+ # Similar to `template_path`, the `view_path` option tells Mustache where to look
71
+ # for files containing view classes when using the `view_class` method.
72
+ #
73
+ module MustacheRender
74
+ class Mustache
75
+
76
+ #
77
+ # Public API
78
+ #
79
+
80
+ # Instantiates an instance of this class and calls `render` with
81
+ # the passed args.
82
+ #
83
+ # Returns a rendered String version of a template
84
+ def self.render(*args)
85
+ new.render(*args)
86
+ end
87
+
88
+ class << self
89
+ alias_method :to_html, :render
90
+ alias_method :to_text, :render
91
+ end
92
+
93
+ # Parses our fancy pants template file and returns normal file with
94
+ # all special {{tags}} and {{#sections}}replaced{{/sections}}.
95
+ #
96
+ # data - A String template or a Hash context. If a Hash is given,
97
+ # we'll try to figure out the template from the class.
98
+ # ctx - A Hash context if `data` is a String template.
99
+ #
100
+ # Examples
101
+ #
102
+ # @view.render("Hi {{thing}}!", :thing => :world)
103
+ #
104
+ # View.template = "Hi {{thing}}!"
105
+ # @view = View.new
106
+ # @view.render(:thing => :world)
107
+ #
108
+ # Returns a rendered String version of a template
109
+ def render(data = template, ctx = {})
110
+ if data.is_a? Hash
111
+ ctx = data
112
+ tpl = templateify(template)
113
+ elsif data.is_a? Symbol
114
+ self.template_name = data
115
+ tpl = templateify(template)
116
+ else
117
+ tpl = templateify(data)
118
+ end
119
+
120
+ return tpl.render(context) if ctx == {}
121
+
122
+ begin
123
+ context.push(ctx)
124
+ tpl.render(context)
125
+ ensure
126
+ context.pop
127
+ end
128
+ end
129
+
130
+ alias_method :to_html, :render
131
+ alias_method :to_text, :render
132
+
133
+ # Context accessors.
134
+ #
135
+ # view = Mustache.new
136
+ # view[:name] = "Jon"
137
+ # view.template = "Hi, {{name}}!"
138
+ # view.render # => "Hi, Jon!"
139
+ def [](key)
140
+ context[key.to_sym]
141
+ end
142
+
143
+ def []=(key, value)
144
+ context[key.to_sym] = value
145
+ end
146
+
147
+ # A helper method which gives access to the context at a given time.
148
+ # Kind of a hack for now, but useful when you're in an iterating section
149
+ # and want access to the hash currently being iterated over.
150
+ def context
151
+ @context ||= Context.new(self)
152
+ end
153
+
154
+ # Given a file name and an optional context, attempts to load and
155
+ # render the file as a template.
156
+ def self.render_file(name, context = {})
157
+ render(partial(name), context)
158
+ end
159
+
160
+ # Given a file name and an optional context, attempts to load and
161
+ # render the file as a template.
162
+ def render_file(name, context = {})
163
+ self.class.render_file(name, context)
164
+ end
165
+
166
+ def self.read_template(name)
167
+ db_template = ::MustacheRenderTemplate.find_with_full_path(name)
168
+ db_template.try :content
169
+ end
170
+
171
+ # Given a name, attempts to read a file and return the contents as a
172
+ # string. The file is not rendered, so it might contain
173
+ # {{mustaches}}.
174
+ #
175
+ # Call `render` if you need to process it.
176
+ def self.partial(name)
177
+ self.read_template(name)
178
+
179
+ # File.read("#{template_path}/#{name}.#{template_extension}")
180
+ end
181
+
182
+ # Override this in your subclass if you want to do fun things like
183
+ # reading templates from a database. It will be rendered by the
184
+ # context, so all you need to do is return a string.
185
+ def partial(name)
186
+ self.class.partial(name)
187
+ end
188
+
189
+ # Override this to provide custom escaping.
190
+ #
191
+ # class PersonView < Mustache
192
+ # def escapeHTML(str)
193
+ # my_html_escape_method(str)
194
+ # end
195
+ # end
196
+ #
197
+ # Returns a String
198
+ def escapeHTML(str)
199
+ CGI.escapeHTML(str)
200
+ end
201
+
202
+
203
+ #
204
+ # Private API
205
+ #
206
+
207
+ # When given a symbol or string representing a class, will try to produce an
208
+ # appropriate view class.
209
+ # e.g.
210
+ # Mustache.view_namespace = Hurl::Views
211
+ # Mustache.view_class(:Partial) # => Hurl::Views::Partial
212
+ def self.view_class(name)
213
+ if name != classify(name.to_s)
214
+ name = classify(name.to_s)
215
+ end
216
+
217
+ # Emptiness begets emptiness.
218
+ if name.to_s == ''
219
+ return Mustache
220
+ end
221
+
222
+ file_name = underscore(name)
223
+
224
+ name = "#{view_namespace}::#{name}"
225
+
226
+ if const = const_get!(name)
227
+ const
228
+ elsif File.exists?(file = "#{view_path}/#{file_name}.rb")
229
+ require "#{file}".chomp('.rb')
230
+ const_get!(name) || Mustache
231
+ else
232
+ Mustache
233
+ end
234
+ end
235
+
236
+ # Supercharged version of Module#const_get.
237
+ #
238
+ # Always searches under Object and can find constants by their full name,
239
+ # e.g. Mustache::Views::Index
240
+ #
241
+ # name - The full constant name to find.
242
+ #
243
+ # Returns the constant if found
244
+ # Returns nil if nothing is found
245
+ def self.const_get!(name)
246
+ name.split('::').inject(Object) do |klass, name|
247
+ klass.const_get(name)
248
+ end
249
+ rescue NameError
250
+ nil
251
+ end
252
+
253
+ # Has this template already been compiled? Compilation is somewhat
254
+ # expensive so it may be useful to check this before attempting it.
255
+ def self.compiled?
256
+ @template.is_a? Template
257
+ end
258
+
259
+ # Has this instance or its class already compiled a template?
260
+ def compiled?
261
+ (@template && @template.is_a?(Template)) || self.class.compiled?
262
+ end
263
+
264
+ # template_partial => TemplatePartial
265
+ # template/partial => Template::Partial
266
+ def self.classify(underscored)
267
+ underscored.split('/').map do |namespace|
268
+ namespace.split(/[-_]/).map do |part|
269
+ part[0] = part[0].chr.upcase; part
270
+ end.join
271
+ end.join('::')
272
+ end
273
+
274
+ # TemplatePartial => template_partial
275
+ # Template::Partial => template/partial
276
+ # Takes a string but defaults to using the current class' name.
277
+ def self.underscore(classified = name)
278
+ classified = name if classified.to_s.empty?
279
+ classified = superclass.name if classified.to_s.empty?
280
+
281
+ string = classified.dup.split("#{view_namespace}::").last
282
+
283
+ string.split('::').map do |part|
284
+ part[0] = part[0].chr.downcase
285
+ part.gsub(/[A-Z]/) { |s| "_#{s.downcase}"}
286
+ end.join('/')
287
+ end
288
+
289
+ # Turns a string into a Mustache::Template. If passed a Template,
290
+ # returns it.
291
+ def self.templateify(obj)
292
+ if obj.is_a?(Template)
293
+ obj
294
+ else
295
+ Template.new(obj.to_s)
296
+ end
297
+ end
298
+
299
+ def templateify(obj)
300
+ self.class.templateify(obj)
301
+ end
302
+
303
+ # Return the value of the configuration setting on the superclass, or return
304
+ # the default.
305
+ #
306
+ # attr_name - Symbol name of the attribute. It should match the instance variable.
307
+ # default - Default value to use if the superclass does not respond.
308
+ #
309
+ # Returns the inherited or default configuration setting.
310
+ def self.inheritable_config_for(attr_name, default)
311
+ superclass.respond_to?(attr_name) ? superclass.send(attr_name) : default
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,144 @@
1
+ module MustacheRender
2
+ class Mustache
3
+ # A ContextMiss is raised whenever a tag's target can not be found
4
+ # in the current context if `Mustache#raise_on_context_miss?` is
5
+ # set to true.
6
+ #
7
+ # For example, if your View class does not respond to `music` but
8
+ # your template contains a `{{music}}` tag this exception will be raised.
9
+ #
10
+ # By default it is not raised. See Mustache.raise_on_context_miss.
11
+ class ContextMiss < RuntimeError; end
12
+
13
+ # A Context represents the context which a Mustache template is
14
+ # executed within. All Mustache tags reference keys in the Context.
15
+ class Context
16
+ # Expect to be passed an instance of `Mustache`.
17
+ def initialize(mustache)
18
+ @stack = [mustache]
19
+ end
20
+
21
+ # A {{>partial}} tag translates into a call to the context's
22
+ # `partial` method, which would be this sucker right here.
23
+ #
24
+ # If the Mustache view handling the rendering (e.g. the view
25
+ # representing your profile page or some other template) responds
26
+ # to `partial`, we call it and render the result.
27
+ def partial(name, indentation = '')
28
+ # Look for the first Mustache in the stack.
29
+ mustache = mustache_in_stack
30
+
31
+ # Indent the partial template by the given indentation.
32
+ part = mustache.partial(name).to_s.gsub(/^/, indentation)
33
+
34
+ # Call the Mustache's `partial` method and render the result.
35
+ result = mustache.render(part, self)
36
+ end
37
+
38
+ # Find the first Mustache in the stack. If we're being rendered
39
+ # inside a Mustache object as a context, we'll use that one.
40
+ def mustache_in_stack
41
+ @stack.detect { |frame| frame.is_a?(Mustache) }
42
+ end
43
+
44
+ # Allows customization of how Mustache escapes things.
45
+ #
46
+ # Returns a String.
47
+ def escapeHTML(str)
48
+ mustache_in_stack.escapeHTML(str)
49
+ end
50
+
51
+ # Adds a new object to the context's internal stack.
52
+ #
53
+ # Returns the Context.
54
+ def push(new)
55
+ @stack.unshift(new)
56
+ self
57
+ end
58
+ alias_method :update, :push
59
+
60
+ # Removes the most recently added object from the context's
61
+ # internal stack.
62
+ #
63
+ # Returns the Context.
64
+ def pop
65
+ @stack.shift
66
+ self
67
+ end
68
+
69
+ # Can be used to add a value to the context in a hash-like way.
70
+ #
71
+ # context[:name] = "Chris"
72
+ def []=(name, value)
73
+ push(name => value)
74
+ end
75
+
76
+ # Alias for `fetch`.
77
+ def [](name)
78
+ fetch(name, nil)
79
+ end
80
+
81
+ # Do we know about a particular key? In other words, will calling
82
+ # `context[key]` give us a result that was set. Basically.
83
+ def has_key?(key)
84
+ !!fetch(key)
85
+ rescue ContextMiss
86
+ false
87
+ end
88
+
89
+ # Similar to Hash#fetch, finds a value by `name` in the context's
90
+ # stack. You may specify the default return value by passing a
91
+ # second parameter.
92
+ #
93
+ # If no second parameter is passed (or raise_on_context_miss is
94
+ # set to true), will raise a ContextMiss exception on miss.
95
+ def fetch(name, default = :__raise)
96
+ @stack.each do |frame|
97
+ # Prevent infinite recursion.
98
+ next if frame == self
99
+
100
+ value = find(frame, name, :__missing)
101
+ if value != :__missing
102
+ return value
103
+ end
104
+ end
105
+
106
+ if default == :__raise || mustache_in_stack.raise_on_context_miss?
107
+ raise ContextMiss.new("Can't find #{name} in #{@stack.inspect}")
108
+ else
109
+ default
110
+ end
111
+ end
112
+
113
+ # Finds a key in an object, using whatever method is most
114
+ # appropriate. If the object is a hash, does a simple hash lookup.
115
+ # If it's an object that responds to the key as a method call,
116
+ # invokes that method. You get the idea.
117
+ #
118
+ # obj - The object to perform the lookup on.
119
+ # key - The key whose value you want.
120
+ # default - An optional default value, to return if the
121
+ # key is not found.
122
+ #
123
+ # Returns the value of key in obj if it is found and default otherwise.
124
+ def find(obj, key, default = nil)
125
+ hash = obj.respond_to?(:has_key?)
126
+
127
+ if hash && obj.has_key?(key)
128
+ obj[key]
129
+ elsif hash && obj.has_key?(key.to_s)
130
+ obj[key.to_s]
131
+ elsif !hash && obj.respond_to?(key)
132
+ meth = obj.method(key) rescue proc { obj.send(key) }
133
+ if meth.arity == 1
134
+ meth.to_proc
135
+ else
136
+ meth[]
137
+ end
138
+ else
139
+ default
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end