tanuki 0.1.3 → 0.2.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. data/README.rdoc +6 -3
  2. data/app/tanuki/attribute/attribute.rb +19 -0
  3. data/app/tanuki/base/base.rb +3 -0
  4. data/app/tanuki/controller/controller.rb +1 -1
  5. data/app/tanuki/controller/link.thtml +6 -6
  6. data/app/tanuki/meta_model/manager.ttxt +2 -0
  7. data/app/tanuki/meta_model/manager_base.ttxt +2 -0
  8. data/app/tanuki/meta_model/meta_model.rb +3 -0
  9. data/app/tanuki/meta_model/model.ttxt +2 -0
  10. data/app/tanuki/meta_model/model_base.ttxt +12 -0
  11. data/app/tanuki/model/model.rb +3 -0
  12. data/app/tanuki/page/missing/default.thtml +61 -2
  13. data/app/user/page/index/default.thtml +120 -120
  14. data/app/user/page/index/index.rb +1 -1
  15. data/bin/tanuki +185 -53
  16. data/config/common.rb +7 -0
  17. data/config/common_application.rb +31 -0
  18. data/config/development_application.rb +8 -0
  19. data/config/production_application.rb +2 -0
  20. data/lib/tanuki/application.rb +15 -19
  21. data/lib/tanuki/argument/base.rb +2 -2
  22. data/lib/tanuki/argument/integer.rb +2 -2
  23. data/lib/tanuki/argument/integer_range.rb +3 -3
  24. data/lib/tanuki/argument/string.rb +2 -2
  25. data/lib/tanuki/argument.rb +3 -3
  26. data/lib/tanuki/{controller_behavior.rb → behavior/controller_behavior.rb} +22 -21
  27. data/lib/tanuki/behavior/meta_model_behavior.rb +43 -0
  28. data/lib/tanuki/behavior/model_behavior.rb +112 -0
  29. data/lib/tanuki/{object_behavior.rb → behavior/object_behavior.rb} +6 -6
  30. data/lib/tanuki/configurator.rb +39 -76
  31. data/lib/tanuki/context.rb +31 -19
  32. data/lib/tanuki/{module_extensions.rb → extensions/module.rb} +3 -3
  33. data/lib/tanuki/extensions/object.rb +12 -0
  34. data/lib/tanuki/extensions/rack/static_dir.rb +18 -0
  35. data/lib/tanuki/i18n.rb +3 -1
  36. data/lib/tanuki/launcher.rb +3 -2
  37. data/lib/tanuki/loader.rb +37 -46
  38. data/lib/tanuki/template_compiler.rb +8 -7
  39. data/lib/tanuki/version.rb +1 -1
  40. data/lib/tanuki.rb +14 -8
  41. data/schema/tanuki/models/controller.yml +2 -3
  42. data/schema/tanuki/models/page.yml +7 -8
  43. metadata +35 -27
  44. data/app/tanuki/object/object.rb +0 -3
@@ -0,0 +1,31 @@
1
+ load_config :common
2
+
3
+ # Rack middleware
4
+ use Rack::Head
5
+ use Rack::StaticDir, 'public'
6
+
7
+ # Server
8
+ set :server, [:thin, :mongrel, :webrick]
9
+ set :host, '0.0.0.0'
10
+ set :port, 3000
11
+
12
+ # Default controllers
13
+ set :root_page, ::User_Page_Index
14
+ set :missing_page, ::Tanuki_Page_Missing
15
+
16
+ # Internationalization
17
+ set :i18n, false
18
+ set :language, nil
19
+ set :language_fallback, {}
20
+ set :languages, proc { language_fallback.keys }
21
+ set :best_language, proc {|lngs| language_fallback[language].each {|lng| return lng if lngs.include? lng }; nil }
22
+ set :best_translation, proc {|trn| language_fallback[language].each {|lng| return trn[lng] if trn.include? lng }; nil }
23
+
24
+ # Visitors
25
+ visitor :string do s = ''; proc {|out| s << out.to_s } end
26
+
27
+ # Argument associations
28
+ argument Fixnum, Argument::Integer
29
+ argument Bignum, Argument::Integer
30
+ argument Range, Argument::IntegerRange
31
+ argument String, Argument::String
@@ -0,0 +1,8 @@
1
+ load_config :common_application
2
+ set :development, true
3
+
4
+ # Rack middleware
5
+ use Rack::CommonLogger
6
+ use Rack::Lint
7
+ use Rack::Reloader, 0
8
+ use Rack::ShowExceptions
@@ -0,0 +1,2 @@
1
+ load_config :common_application
2
+ set :development, false
@@ -4,37 +4,33 @@ module Tanuki
4
4
  # It contains core application functionality like configuration, request handling and view management.
5
5
  class Application
6
6
 
7
- @context = Loader.context = Context.new
8
- @rack_middleware = {}
7
+ @context = (Loader.context = Context).child
8
+ @rack_middleware = []
9
9
 
10
10
  class << self
11
11
 
12
- # Removes a given middleware from the Rack middleware pipeline
12
+ # Removes all occurences of a given +middleware+ from the Rack middleware pipeline.
13
13
  def discard(middleware)
14
- @rack_middleware.delete(middleware)
14
+ @rack_middleware.delete_if {|item| item[0] == middleware }
15
15
  end
16
16
 
17
- # Runs the application with current settings
17
+ # Runs the application with current settings.
18
18
  def run
19
19
  rack_builder = Rack::Builder.new
20
- @rack_middleware.each {|middleware, params| rack_builder.use(middleware, *params[0], &params[1]) }
20
+ @rack_middleware.each {|item| rack_builder.use(item[0], *item[1], &item[2]) }
21
21
  rack_builder.run(rack_app)
22
22
  srv = available_server
23
- puts "A wild Tanuki appears!", "You used #{srv.name.gsub(/.*::/, '')} at #{@context.host}:#{@context.port}."
23
+ puts "A wild Tanuki appears! Press Ctrl-C to set it free.",
24
+ "You used #{srv.name.gsub(/.*::/, '')} at #{@context.host}:#{@context.port}."
24
25
  srv.run rack_builder.to_app, :Host => @context.host, :Port => @context.port
25
26
  end
26
27
 
27
- # Sets an option to value in the current context.
28
- def set(option, value)
29
- @context.send("#{option}=".to_sym, value)
30
- end
31
-
32
- # Adds a given middleware to the Rack middleware pipeline
28
+ # Adds a given +middleware+ with optional +args+ and +block+ to the Rack middleware pipeline.
33
29
  def use(middleware, *args, &block)
34
- @rack_middleware[middleware] = [args, block]
30
+ @rack_middleware << [middleware, args, block]
35
31
  end
36
32
 
37
- # Adds a template visitor block. This block must return a Proc.
33
+ # Adds a template visitor +block+. This +block+ must return a +Proc+.
38
34
  #
39
35
  # visitor :escape do
40
36
  # s = ''
@@ -50,7 +46,7 @@ module Tanuki
50
46
  # <%_escape escaped_view %>
51
47
  # <%_printf('<div>%s</div>') formatted_view %>
52
48
  def visitor(sym, &block)
53
- ObjectBehavior.instance_eval { define_method "#{sym}_visitor".to_sym, &block }
49
+ BaseBehavior.instance_eval { define_method "#{sym}_visitor".to_sym, &block }
54
50
  end
55
51
 
56
52
  private
@@ -67,7 +63,7 @@ module Tanuki
67
63
  raise "servers #{@context.server.join(', ')} not found"
68
64
  end
69
65
 
70
- # Returns an array of template outputs for controller ctrl in context ctx.
66
+ # Returns an array of template outputs for controller +ctrl+ in context +request_ctx+.
71
67
  def build_body(ctrl, request_ctx)
72
68
  arr = []
73
69
  Launcher.new(ctrl, request_ctx).each &proc {|out| arr << out.to_s }
@@ -84,14 +80,14 @@ module Tanuki
84
80
  proc do |env|
85
81
  request_ctx = ctx.child
86
82
  request_ctx.templates = {}
87
- if match = env['REQUEST_PATH'].match(/^(.+)(?<!\$)\/$/)
83
+ if match = env['PATH_INFO'].match(/^(.+)(?<!\$)\/$/)
88
84
  loc = match[1]
89
85
  loc << "?#{env['QUERY_STRING']}" unless env['QUERY_STRING'].empty?
90
86
  [301, {'Location' => loc, 'Content-Type' => 'text/html; charset=utf-8'}, []]
91
87
  else
92
88
  request_ctx.env = env
93
89
  result = ::Tanuki::ControllerBehavior.dispatch(request_ctx, ctx.i18n ? ::Tanuki::I18n : ctx.root_page,
94
- Rack::Utils.unescape(env['REQUEST_PATH']).force_encoding('UTF-8'))
90
+ Rack::Utils.unescape(env['PATH_INFO']).force_encoding('UTF-8'))
95
91
  case result[:type]
96
92
  when :redirect then
97
93
  [302, {'Location' => result[:location], 'Content-Type' => 'text/html; charset=utf-8'}, []]
@@ -6,7 +6,7 @@ module Tanuki
6
6
 
7
7
  attr_reader :default, :value
8
8
 
9
- # Initializes the argument with a default value.
9
+ # Initializes the argument with a +default+ value.
10
10
  def initialize(default)
11
11
  @value = @default = default
12
12
  end
@@ -16,7 +16,7 @@ module Tanuki
16
16
  @value.to_s
17
17
  end
18
18
 
19
- # Sets the value with required rules.
19
+ # Sets the value to +obj+ with required rules.
20
20
  def value=(obj)
21
21
  @value = to_value(obj)
22
22
  end
@@ -1,10 +1,10 @@
1
1
  module Tanuki
2
2
  module Argument
3
3
 
4
- # Tanuki::Argument::Integer is a class for Integer arguments.
4
+ # Tanuki::Argument::Integer is a class for +Integer+ arguments.
5
5
  class Integer < Base
6
6
 
7
- # Returns argument value from a string representation.
7
+ # Returns argument value from an object +obj+.
8
8
  def to_value(obj)
9
9
  begin Kernel::Integer obj rescue @default end
10
10
  end
@@ -1,16 +1,16 @@
1
1
  module Tanuki
2
2
  module Argument
3
3
 
4
- # Tanuki::Argument::IntegerRange is a class for Integer arguments with a certain value range.
4
+ # Tanuki::Argument::IntegerRange is a class for +Integer+ arguments with a certain value range.
5
5
  class IntegerRange < Integer
6
6
 
7
- # Initializes the argument with a default value and allowed value range.
7
+ # Initializes the argument with a +default+ value and allowed value +range+.
8
8
  def initialize(range, default=nil)
9
9
  super(default ? default : range.first)
10
10
  @range = range
11
11
  end
12
12
 
13
- # Returns argument value from a string representation.
13
+ # Returns argument value from an object +obj+.
14
14
  def to_value(obj)
15
15
  i = super(obj)
16
16
  @range.include?(i) ? i : @default
@@ -1,10 +1,10 @@
1
1
  module Tanuki
2
2
  module Argument
3
3
 
4
- # Tanuki::Argument::String is a class for String arguments.
4
+ # Tanuki::Argument::String is a class for +String+ arguments.
5
5
  class String < Base
6
6
 
7
- # Returns argument value from a string representation.
7
+ # Returns argument value from an object +obj+.
8
8
  def to_value(obj)
9
9
  obj.to_s
10
10
  end
@@ -12,12 +12,12 @@ module Tanuki
12
12
 
13
13
  class << self
14
14
 
15
- # Remove argument association for a given type class.
15
+ # Removes argument association for a given type class +klass+.
16
16
  def delete(klass)
17
17
  @assoc.delete(klass)
18
18
  end
19
19
 
20
- # Associate a given type class with an argument class.
20
+ # Associates a given type class +klass+ with an argument class +arg_class+.
21
21
  def store(klass, arg_class)
22
22
  warn "Tanuki::Argument::Base is not an ancestor of `#{arg_class}'" unless arg_class.ancestors.include? Argument::Base
23
23
  @assoc[klass] = arg_class
@@ -25,7 +25,7 @@ module Tanuki
25
25
 
26
26
  alias_method :[], :store
27
27
 
28
- # Convert a given type object to an argument object.
28
+ # Converts a given type object +obj+ to an argument object with optional +args+.
29
29
  def to_argument(obj, *args)
30
30
  if @assoc.include?(klass = obj.class)
31
31
  @assoc[klass].new(obj, *args)
@@ -9,7 +9,7 @@ module Tanuki
9
9
  internal_attr_reader :model, :logical_parent, :link
10
10
  internal_attr_accessor :logical_child, :visual_child
11
11
 
12
- # Create new controller with context ctx, logical_parent controller, route_part definitions and a model.
12
+ # Creates new controller with context +ctx+, +logical_parent+ controller, +route_part+ definitions and a +model+.
13
13
  def initialize(ctx, logical_parent, route_part, model=nil)
14
14
  @_configured = false
15
15
  @_ctx = ctx
@@ -29,7 +29,7 @@ module Tanuki
29
29
  end
30
30
  end
31
31
 
32
- # Invoked with route args when current route is initialized.
32
+ # Invoked with route +args+ when current route is initialized.
33
33
  def initialize_route(*args)
34
34
  end
35
35
 
@@ -38,7 +38,7 @@ module Tanuki
38
38
  @_ctx
39
39
  end
40
40
 
41
- # Initializes and retrieves child route object. Searches static, dynamic, and ghost routes (in that order).
41
+ # Initializes and retrieves child controller on +route+. Searches static, dynamic, and ghost routes (in that order).
42
42
  def [](route, *args)
43
43
  byname = (args.length == 1 and args[0].is_a? Hash)
44
44
  ensure_configured!
@@ -77,12 +77,12 @@ module Tanuki
77
77
  @_cache[key] = child # Thread safe (possible overwrite, but within consistent state)
78
78
  end
79
79
 
80
- # Return true, if controller is active.
80
+ # Returns true, if controller is active.
81
81
  def active?
82
82
  @_active
83
83
  end
84
84
 
85
- # Retrieves child route class. Searches static, dynamic, and ghost routes (in that order).
85
+ # Retrieves child controller class on +route+. Searches static, dynamic, and ghost routes (in that order).
86
86
  def child_class(route)
87
87
  ensure_configured!
88
88
  args = []
@@ -108,11 +108,11 @@ module Tanuki
108
108
  end
109
109
  end
110
110
 
111
- # Invoked when controller need to be configured.
111
+ # Invoked when controller needs to be configured.
112
112
  def configure
113
113
  end
114
114
 
115
- # Return true, if controller is current.
115
+ # Returns true, if controller is current.
116
116
  def current?
117
117
  @_current
118
118
  end
@@ -122,6 +122,7 @@ module Tanuki
122
122
  nil
123
123
  end
124
124
 
125
+ # Calls +block+ once for each visible child controller on static or dynamic routes, passing it as a parameter.
125
126
  def each(&block)
126
127
  return Enumerator.new(self) unless block_given?
127
128
  ensure_configured!
@@ -157,15 +158,15 @@ module Tanuki
157
158
  nil
158
159
  end
159
160
 
160
- # Returns the link to the current controller, switching the active controller on the respective path level to self.
161
+ # Returns the link to the current controller, switching the active controller on the respective path level to +self+.
161
162
  def forward_link
162
- uri_parts = @_ctx.env['REQUEST_PATH'].split(/(?<!\$)\//)
163
+ uri_parts = @_ctx.env['PATH_INFO'].split(/(?<!\$)\//)
163
164
  link_parts = link.split(/(?<!\$)\//)
164
165
  link_parts.each_index {|i| uri_parts[i] = link_parts[i] }
165
166
  uri_parts.join('/') << ((qs = @_ctx.env['QUERY_STRING']).empty? ? '' : "?#{qs}")
166
167
  end
167
168
 
168
- # Returns the number of children.
169
+ # Returns the number of visible child controllers on static and dynamic routes.
169
170
  def length
170
171
  if @_child_collection_defs.length > 0
171
172
  if @_length_is_valid
@@ -179,7 +180,7 @@ module Tanuki
179
180
  end
180
181
  end
181
182
 
182
- # Process context passed to child
183
+ # Invoked when child controller context needs to be processed before initializing.
183
184
  def process_child_context(ctx, route)
184
185
  ctx
185
186
  end
@@ -198,21 +199,21 @@ module Tanuki
198
199
 
199
200
  private
200
201
 
201
- # Defines a child of class klass on route with model, optionally hidden.
202
+ # Defines a child of class +klass+ on +route+ with +model+, optionally +hidden+.
202
203
  def has_child(klass, route, model=nil, hidden=false)
203
204
  @_child_defs[route] = {:class => klass, :model => model, :hidden => hidden}
204
205
  @_length += 1 unless hidden
205
206
  self
206
207
  end
207
208
 
208
- # Defines a child collection of type parse_regexp.
209
+ # Defines a child collection of type +parse_regexp+, formatted back by +format_string+.
209
210
  def has_child_collection(parse_regexp, format_string, child_def_fetcher)
210
211
  @_child_defs[parse_regexp] = @_child_collection_defs.size
211
212
  @_child_collection_defs << {:parse => parse_regexp, :format => format_string, :fetcher => child_def_fetcher}
212
213
  @_length_is_valid = false
213
214
  end
214
215
 
215
- # Invoked for route with args when a route is missing.
216
+ # Invoked for +route+ with +args+ when a route is missing. This hook can be used to make ghost routes.
216
217
  def missing_route(route, *args)
217
218
  @_ctx.missing_page.new(@_ctx, self, {:route => route, :args => []})
218
219
  end
@@ -225,12 +226,12 @@ module Tanuki
225
226
  @_arg_defs ||= superclass.arg_defs.dup
226
227
  end
227
228
 
228
- # Escapes a given string for use in links.
229
+ # Escapes characters +chrs+ and encodes a given string +s+ for use in links.
229
230
  def escape(s, chrs)
230
231
  s ? Rack::Utils.escape(s.to_s.gsub(/[\$#{chrs}]/, '$\0')) : nil
231
232
  end
232
233
 
233
- # Extracts arguments, initializing default values beforehand. Searches md hash for default value overrides.
234
+ # Extracts arguments, initializing default values beforehand. Searches +md+ hash for default value overrides.
234
235
  def extract_args(md)
235
236
  res = []
236
237
  arg_defs.each_pair do |name, arg|
@@ -239,7 +240,7 @@ module Tanuki
239
240
  res
240
241
  end
241
242
 
242
- # Builds link from root to self.
243
+ # Builds link from controller +ctrl+ to a given route.
243
244
  def grow_link(ctrl, route_part, arg_defs)
244
245
  own_link = escape(route_part[:route], '\/:') << route_part[:args].map do |k, v|
245
246
  arg_defs[k][:arg].default == v ? '' : ":#{escape(k, '\/:-')}-#{escape(v, '\/:')}"
@@ -247,7 +248,7 @@ module Tanuki
247
248
  "#{ctrl.link == '/' ? '' : ctrl.link}/#{own_link}"
248
249
  end
249
250
 
250
- # Defines an argument with name, derived from object obj with additional args.
251
+ # Defines an argument with a +name+, derived from type +obj+ with additional +args+.
251
252
  def has_arg(name, obj, *args)
252
253
  # TODO Ensure thread safety
253
254
  arg_defs[name] = {:arg => Argument.to_argument(obj, *args), :index => @_arg_defs.size}
@@ -264,7 +265,7 @@ module Tanuki
264
265
 
265
266
  class << self
266
267
 
267
- # Dispathes route chain in context ctx on request_path, starting with controller klass.
268
+ # Dispathes route chain in context +ctx+ on +request_path+, starting with controller +klass+.
268
269
  def dispatch(ctx, klass, request_path)
269
270
  parts = parse_path(request_path)
270
271
  curr = root_ctrl = klass.new(ctx, nil, nil, true)
@@ -297,7 +298,7 @@ module Tanuki
297
298
 
298
299
  private
299
300
 
300
- # Parses path to return route name and arguments.
301
+ # Parses +path+ to return route name and arguments.
301
302
  def parse_path(path)
302
303
  path[1..-1].split(/(?<!\$)\//).map do |s|
303
304
  arr = s.gsub('$/', '/').split(/(?<!\$):/)
@@ -312,7 +313,7 @@ module Tanuki
312
313
  end
313
314
  end
314
315
 
315
- # Unescape a given link part for internal use.
316
+ # Unescapes a given link part for internal use.
316
317
  def unescape(s)
317
318
  s ? s.gsub(/\$([\/\$:-])/, '\1') : nil
318
319
  end
@@ -0,0 +1,43 @@
1
+ module Tanuki
2
+
3
+ module MetaModelBehavior
4
+
5
+ def initialize(namespace, name, data)
6
+ @namespace = namespace
7
+ @name = name
8
+ @data = data
9
+ end
10
+
11
+ def class_name_for(class_type)
12
+ case class_type
13
+ when :model, :model_base then "#{@namespace}_Model_#{@name}"
14
+ when :manager, :manager_base then "#{@namespace}_Manager_#{@name}"
15
+ end
16
+ end
17
+
18
+ def key
19
+ if @data['key'].nil?
20
+ []
21
+ elsif @data['key'].is_a? Array
22
+ @data['key'].map {|item| qualified_name(item) }
23
+ elsif @data['key'].is_a? String
24
+ [qualified_name(@data['key'])]
25
+ else
26
+ raise "key for model #{@namespace}.#{@name} is invalid"
27
+ end
28
+ end
29
+
30
+ def qualified_name(field_name)
31
+ parts = field_name.split('.')
32
+ if parts.length == 1
33
+ ":#{field_name}"
34
+ elsif parts.length == 2
35
+ ":#{parts[1]}.qualify(:#{parts[0]}"
36
+ else
37
+ raise "field name for model #{@namespace}.#{@name} is invalid"
38
+ end
39
+ end
40
+
41
+ end # end MetaModelBehavior
42
+
43
+ end # end Tanuki
@@ -0,0 +1,112 @@
1
+ module Tanuki
2
+
3
+ module ModelBehavior
4
+
5
+ def initialize(data = {}, lazy = false)
6
+ @_data = data
7
+ @_loaded = !lazy
8
+ end
9
+
10
+ def [](attribute)
11
+ ensure_loaded!
12
+ self.class[attribute].get(@_data)
13
+ end
14
+
15
+ def []=(attribute, value)
16
+ @_errors ||= {}
17
+ @_original ||= {}
18
+ begin
19
+ @_original[attribute] = self[attribute] unless @_original.include? attribute
20
+ self.class[attribute].set(@_data, value)
21
+ @_errors.delete(attribute)
22
+ rescue
23
+ @_errors[attribute] = {:value => value, :error => $!}
24
+ end
25
+ end
26
+
27
+ def internals_get(attribute)
28
+ self.class[attribute].internals_get(@_data)
29
+ end
30
+
31
+ def internals_set(attribute, internal_value)
32
+ @_errors ||= {}
33
+ internals_set(self.class[attribute], @_data)
34
+ end
35
+
36
+ def get_updates
37
+ @_original ||= {}
38
+ original_data = {}
39
+ self.class.attributes.each_pair do |name, attrib|
40
+ attrib.set(original_data, @_original[name])
41
+ end
42
+ updates = {}
43
+ original_data.each_pair do |field, value|
44
+ updates[field] = data[field] if data[field] != original_data[field]
45
+ end
46
+ updates
47
+ end
48
+
49
+ def get_error(attribute)
50
+ @_errors ||= {}
51
+ @_errors[attribute]
52
+ end
53
+
54
+ def invalid?(attribute)
55
+ @_errors.include? attribute
56
+ end
57
+
58
+ def has_errors?
59
+ @_errors ||= {}
60
+ @_errors == {}
61
+ end
62
+
63
+ def errors
64
+ @_errors ||= {}
65
+ @_errors
66
+ end
67
+
68
+ module ClassMethods
69
+
70
+ def create(data, ctx, lazy = false) # IDENTITY TRACKING AND LAZY LOADING
71
+ entity_key = extract_key(data)
72
+ key = [self, entity_key] #extract_key is generated ad hoc by model compiler!
73
+ if cached = ctx.entity_cache[key]
74
+ cached
75
+ else
76
+ ctx.entity_cache[key] = get(*entity_key) # get is generated Ad Hoc by model compiler
77
+ end
78
+ end
79
+
80
+ def has_attribute(attribute, attr_def)
81
+ @_attributes ||= superclass.instance_variable_get(:@_attributes).dup
82
+ @_attributes[attribute] = attr_def
83
+ end
84
+
85
+ def [](attribute)
86
+ @_attributes[attribute]
87
+ end
88
+
89
+ def has_reference(attribute, reference_def)
90
+ @_references ||= superclass.instance_variable_get(:@_references).dup
91
+ @_references[attribute] = reference_def
92
+ end
93
+
94
+ # Prepares the extended module.
95
+ def self.extended(mod)
96
+ mod.instance_variable_set(:@_attributes, {})
97
+ mod.instance_variable_set(:@_references, {})
98
+ end
99
+
100
+ end # end ClassMethods
101
+
102
+ class << self
103
+
104
+ def included(mod)
105
+ mod.extend ClassMethods
106
+ end
107
+
108
+ end # end class << self
109
+
110
+ end # end ModelBehaviour
111
+
112
+ end # end Tanuki
@@ -1,15 +1,15 @@
1
1
  module Tanuki
2
2
 
3
- # Tanuki::ControllerBehavior contains basic methods for a framework object.
3
+ # Tanuki::BaseBehavior contains basic methods for a templatable object.
4
4
  # In is included in the base framework object class.
5
- module ObjectBehavior
5
+ module BaseBehavior
6
6
 
7
- # Shortcut to Tanuki::Loader.has_template?. Used internally by templates.
7
+ # Shortcut to Tanuki::Loader::has_template?. Used internally by templates.
8
8
  def _has_tpl(ctx, klass, sym)
9
9
  Tanuki::Loader.has_template?(ctx.templates, klass, sym)
10
10
  end
11
11
 
12
- # Shortcut to Tanuki::Loader.run_template. Used internally by templates.
12
+ # Shortcut to Tanuki::Loader::run_template. Used internally by templates.
13
13
  def _run_tpl(ctx, obj, sym, *args, &block)
14
14
  Tanuki::Loader.run_template(ctx.templates, obj, sym, *args, &block)
15
15
  end
@@ -19,7 +19,7 @@ module Tanuki
19
19
  ctx
20
20
  end
21
21
 
22
- # Kernel#method_missing hook for fetching views.
22
+ # Allows to return template blocks. E. g. returns +foobar+ template block when +foobar_view+ method is called.
23
23
  def method_missing(sym, *args, &block)
24
24
  if matches = sym.to_s.match(/^(.*)_view$/)
25
25
  return Tanuki::Loader.run_template({}, self, matches[1].to_sym, *args, &block)
@@ -27,6 +27,6 @@ module Tanuki
27
27
  super
28
28
  end
29
29
 
30
- end # end ObjectBehavior
30
+ end # end BaseBehavior
31
31
 
32
32
  end # end Tanuki