dyoder-waves 0.7.3 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/app/bin/waves-console +1 -0
  2. data/app/bin/waves-server +1 -0
  3. data/app/configurations/development.rb.erb +5 -6
  4. data/app/configurations/mapping.rb.erb +1 -2
  5. data/app/configurations/production.rb.erb +2 -2
  6. data/app/controllers/.gitignore +0 -0
  7. data/app/doc/.gitignore +0 -0
  8. data/app/helpers/.gitignore +0 -0
  9. data/app/lib/application.rb.erb +4 -2
  10. data/app/lib/tasks/.gitignore +0 -0
  11. data/app/log/.gitignore +0 -0
  12. data/app/models/.gitignore +0 -0
  13. data/app/public/css/.gitignore +0 -0
  14. data/app/public/flash/.gitignore +0 -0
  15. data/app/public/images/.gitignore +0 -0
  16. data/app/public/javascript/.gitignore +0 -0
  17. data/app/schema/migrations/.gitignore +0 -0
  18. data/app/tmp/sessions/.gitignore +0 -0
  19. data/app/views/.gitignore +0 -0
  20. data/bin/waves +29 -46
  21. data/bin/waves-console +1 -1
  22. data/bin/waves-server +1 -1
  23. data/lib/commands/waves-console.rb +0 -3
  24. data/lib/controllers/base.rb +11 -0
  25. data/lib/controllers/mixin.rb +39 -32
  26. data/lib/dispatchers/base.rb +37 -22
  27. data/lib/dispatchers/default.rb +25 -11
  28. data/lib/foundations/default.rb +6 -8
  29. data/lib/foundations/simple.rb +13 -0
  30. data/lib/helpers/asset_helper.rb +67 -0
  31. data/lib/helpers/common.rb +4 -0
  32. data/lib/helpers/default.rb +13 -0
  33. data/lib/helpers/form.rb +1 -0
  34. data/lib/helpers/number_helper.rb +25 -0
  35. data/lib/helpers/tag_helper.rb +58 -0
  36. data/lib/helpers/url_helper.rb +77 -0
  37. data/lib/layers/default_errors.rb +7 -5
  38. data/lib/layers/mvc.rb +54 -0
  39. data/lib/layers/orm/active_record.rb +93 -0
  40. data/lib/layers/orm/active_record/migrations/empty.rb.erb +9 -0
  41. data/lib/layers/orm/active_record/tasks/generate.rb +28 -0
  42. data/lib/layers/orm/active_record/tasks/schema.rb +22 -0
  43. data/lib/layers/orm/data_mapper.rb +38 -0
  44. data/lib/layers/orm/filebase.rb +22 -0
  45. data/lib/layers/orm/migration.rb +79 -0
  46. data/lib/layers/orm/sequel.rb +86 -0
  47. data/lib/layers/orm/sequel/migrations/empty.rb.erb +9 -0
  48. data/lib/layers/orm/sequel/tasks/generate.rb +28 -0
  49. data/lib/layers/orm/sequel/tasks/schema.rb +16 -0
  50. data/lib/layers/simple.rb +35 -0
  51. data/lib/layers/simple_errors.rb +11 -5
  52. data/lib/mapping/mapping.rb +61 -24
  53. data/lib/mapping/pretty_urls.rb +5 -3
  54. data/lib/renderers/erubis.rb +3 -1
  55. data/lib/renderers/mixin.rb +1 -4
  56. data/lib/runtime/application.rb +12 -8
  57. data/lib/runtime/blackboard.rb +57 -0
  58. data/lib/runtime/configuration.rb +60 -55
  59. data/lib/runtime/logger.rb +8 -1
  60. data/lib/runtime/request.rb +5 -4
  61. data/lib/runtime/response.rb +3 -3
  62. data/lib/runtime/response_mixin.rb +3 -0
  63. data/lib/runtime/response_proxy.rb +4 -1
  64. data/lib/runtime/server.rb +18 -5
  65. data/lib/runtime/session.rb +20 -10
  66. data/lib/tasks/cluster.rb +2 -1
  67. data/lib/tasks/generate.rb +76 -11
  68. data/lib/utilities/hash.rb +31 -0
  69. data/lib/utilities/inflect.rb +86 -170
  70. data/lib/utilities/inflect/english.rb +84 -0
  71. data/lib/utilities/integer.rb +23 -16
  72. data/lib/utilities/module.rb +19 -15
  73. data/lib/utilities/object.rb +22 -14
  74. data/lib/utilities/proc.rb +13 -6
  75. data/lib/utilities/string.rb +58 -44
  76. data/lib/utilities/symbol.rb +4 -1
  77. data/lib/views/base.rb +9 -0
  78. data/lib/views/mixin.rb +3 -1
  79. data/lib/waves.rb +15 -13
  80. metadata +50 -25
  81. data/lib/utilities/kernel.rb +0 -34
  82. data/lib/verify/mapping.rb +0 -29
  83. data/lib/verify/request.rb +0 -40
@@ -1,6 +1,10 @@
1
- require 'layers/orm/sequel'
2
1
  module Waves
3
2
  module Foundations
3
+ # The Default foundation supports the common MVC development pattern, a la Rails and Merb. Models, controllers, views, templates, and helpers live in the corresponding directories. When your code calls for a specific M, V, C, or H, Waves tries to load it from a file matching the snake-case of the constant name. If the file does not exist, Waves creates the constant from a sensible (and customizable) default.
4
+ #
5
+ # This foundation does not include any ORM configuration. You can include Waves::Layers::ORM::Sequel or custom configure your model.
6
+
7
+
4
8
  module Default
5
9
 
6
10
  def self.included( app )
@@ -8,14 +12,8 @@ module Waves
8
12
  app.instance_eval do
9
13
 
10
14
  include Waves::Layers::Simple
15
+ include Waves::Layers::MVC
11
16
  include Waves::Layers::DefaultErrors
12
- include Waves::Layers::MVC
13
- include Waves::Layers::ORM::Sequel
14
-
15
- # Set autoloading from default.rb files
16
- #autoinit :Configurations do
17
- # autoload_class true
18
- #end
19
17
 
20
18
  end
21
19
 
@@ -1,8 +1,21 @@
1
1
  module Waves
2
+
3
+ # A Waves Foundation provides enough functionality to allow a Waves application
4
+ # to run. At the bare minimum, this means creating configuration classes in the Configurations
5
+ # namespace, as is done in the Simple foundation
6
+ #
7
+ # Typically, a Foundation will include several Layers, perform any necessary
8
+ # configuration, and register the application with the Waves module
2
9
  module Foundations
3
10
 
11
+ # The Simple foundation provides the bare minimum needed to run a Waves application.
12
+ # It is intended for use as the basis of more fully-featured foundations, but you can
13
+ # use it as a standalone where all the request processing is done directly in a
14
+ # mapping lambda.
4
15
  module Simple
5
16
 
17
+ # On inclusion in a module, the Simple foundation includes Waves::Layers::Simple and
18
+ # registers the module as a Waves application.
6
19
  def self.included( app )
7
20
 
8
21
  app.instance_eval do
@@ -0,0 +1,67 @@
1
+ module Waves
2
+ module Helpers
3
+ module AssetHelper
4
+ # Returns an html image tag for the +source+. The +source+ can be a full
5
+ # path or a file that exists in your public images directory. Note that
6
+ # specifying a filename without the extension is now deprecated in Rails.
7
+ # You can add html attributes using the +options+. The +options+ supports
8
+ # two additional keys for convienence and conformance:
9
+ #
10
+ # * <tt>:alt</tt> - If no alt text is given, the file name part of the
11
+ # +source+ is used (capitalized and without the extension)
12
+ # * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
13
+ # width="30" and height="45". <tt>:size</tt> will be ignored if the
14
+ # value is not in the correct format.
15
+ #
16
+ # image_tag("icon.png") # =>
17
+ # <img src="/images/icon.png" alt="Icon" />
18
+ # image_tag("icon.png", :size => "16x10", :alt => "Edit Entry") # =>
19
+ # <img src="/images/icon.png" width="16" height="10" alt="Edit Entry" />
20
+ # image_tag("/icons/icon.gif", :size => "16x16") # =>
21
+ # <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
22
+ def image_tag(source, options = {})
23
+ options.symbolize_keys!
24
+
25
+ options[:src] = image_path(source)
26
+ options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
27
+
28
+ if options[:size]
29
+ options[:width], options[:height] = options[:size].split("x") if options[:size] =~ %r{^\d+x\d+$}
30
+ options.delete(:size)
31
+ end
32
+
33
+ tag("img", options)
34
+ end
35
+
36
+ # Computes the path to an image asset in the public images directory.
37
+ # Full paths from the document root will be passed through.
38
+ # Used internally by image_tag to build the image path. Passing
39
+ # a filename without an extension is deprecated.
40
+ #
41
+ # image_path("edit.png") # => /images/edit.png
42
+ # image_path("icons/edit.png") # => /images/icons/edit.png
43
+ # image_path("/icons/edit.png") # => /icons/edit.png
44
+ def image_path(source)
45
+ compute_public_path(source, 'images', 'png')
46
+ end
47
+
48
+ private
49
+ def compute_public_path(source, dir, ext)
50
+ source = source.dup
51
+ source << ".#{ext}" if File.extname(source).blank?
52
+ unless source =~ %r{^[-a-z]+://}
53
+ source = "/#{dir}/#{source}" unless source[0] == ?/
54
+ asset_id = rails_asset_id(source)
55
+ source << '?' + asset_id if defined?(RAILS_ROOT) && !asset_id.blank?
56
+ # source = "#{ActionController::Base.asset_host}#{@controller.request.relative_url_root}#{source}"
57
+ end
58
+ source
59
+ end
60
+
61
+ def rails_asset_id(source)
62
+ ENV["WAVES_ASSET_ID"] || File.mtime("public/#{source}").to_i.to_s rescue ""
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -1,4 +1,8 @@
1
1
  module Waves
2
+
3
+ # Helper methods can be defined for any view template by simply defining them within the default Helper module in <tt>helpers/default.rb</tt> of the generated application. Helpers specific to a particular View class can be explicitly defined by creating a helper module that corresponds to the View class. For examples, for the +User+ View class, you would define a helper module in <tt>user.rb</tt> named +User+.
4
+ #
5
+ # The default helper class initially includes a wide-variety of helpers, including helpers for layouts, Textile formatting, rendering forms, and nested views, as well as helpers for accessing the request and response objects. More helpers will be added in future releases, but in many cases, there is no need to include all of them in your application.
2
6
  module Helpers
3
7
 
4
8
  # Common helpers are helpers that are needed for just about any Web page. For example,
@@ -0,0 +1,13 @@
1
+ module Waves
2
+ module Helpers
3
+ module Default
4
+ attr_reader :request, :content
5
+ include Waves::ResponseMixin
6
+ include Waves::Helpers::Common
7
+ include Waves::Helpers::Formatting
8
+ include Waves::Helpers::Model
9
+ include Waves::Helpers::View
10
+ include Waves::Helpers::Form
11
+ end
12
+ end
13
+ end
data/lib/helpers/form.rb CHANGED
@@ -15,6 +15,7 @@ module Waves
15
15
  # will invoke the +text+ form view (the template in +templates/form/text.mab+),
16
16
  # passing in the name ('blog.title') and the value (@blog.title) as instance variables.
17
17
  #
18
+ # These helpers are Markaby only.
18
19
  module Form
19
20
 
20
21
  # This method really is a place-holder for common wrappers around groups of
@@ -0,0 +1,25 @@
1
+ module Waves
2
+ module Helpers
3
+ module NumberHelper
4
+
5
+ # Formats a +number+ with grouped thousands using +delimiter+. You
6
+ # can customize the format in the +options+ hash.
7
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter, defaults to ","
8
+ # * <tt>:separator</tt> - Sets the separator between the units, defaults to "."
9
+ #
10
+ # number_with_delimiter(12345678) => 12,345,678
11
+ # number_with_delimiter(12345678.05) => 12,345,678.05
12
+ # number_with_delimiter(12345678, :delimiter => ".") => 12.345.678
13
+ def number_with_delimiter(number, delimiter=",", separator=".")
14
+ begin
15
+ parts = number.to_s.split(separator)
16
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
17
+ parts.join separator
18
+ rescue
19
+ number
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,58 @@
1
+ module Waves
2
+ module Helpers
3
+ module TagHelper
4
+
5
+ ESCAPE_TABLE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
6
+ def h(value)
7
+ value.to_s.gsub(/[&<>"]/) { |s| ESCAPE_TABLE[s] }
8
+ end
9
+
10
+ # Returns an empty HTML tag of type +name+ which by default is XHTML
11
+ # compliant. Setting +open+ to true will create an open tag compatible
12
+ # with HTML 4.0 and below. Add HTML attributes by passing an attributes
13
+ # hash to +options+. For attributes with no value like (disabled and
14
+ # readonly), give it a value of true in the +options+ hash. You can use
15
+ # symbols or strings for the attribute names.
16
+ #
17
+ # tag("br")
18
+ # # => <br />
19
+ # tag("br", nil, true)
20
+ # # => <br>
21
+ # tag("input", { :type => 'text', :disabled => true })
22
+ # # => <input type="text" disabled="disabled" />
23
+ def tag(name, options = nil, open = false)
24
+ "<#{name}#{tag_options(options) if options}" + (open ? ">" : " />")
25
+ end
26
+
27
+ # Returns the escaped +html+ without affecting existing escaped entities.
28
+ #
29
+ # escape_once("1 > 2 &amp; 3")
30
+ # # => "1 &lt; 2 &amp; 3"
31
+ def escape_once(html)
32
+ fix_double_escape(h(html.to_s))
33
+ end
34
+
35
+ private
36
+
37
+ def tag_options(options)
38
+ cleaned_options = convert_booleans(options.stringify_keys.reject {|key, value| value.nil?})
39
+ ' ' + cleaned_options.map {|key, value| %(#{key}="#{escape_once(value)}")}.sort * ' ' unless cleaned_options.empty?
40
+ end
41
+
42
+ def convert_booleans(options)
43
+ %w( disabled readonly multiple ).each { |a| boolean_attribute(options, a) }
44
+ options
45
+ end
46
+
47
+ def boolean_attribute(options, attribute)
48
+ options[attribute] ? options[attribute] = attribute : options.delete(attribute)
49
+ end
50
+
51
+ # Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc.
52
+ def fix_double_escape(escaped)
53
+ escaped.gsub(/&amp;([a-z]+|(#\d+));/i) { "&#{$1};" }
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,77 @@
1
+ module Waves
2
+ module Helpers
3
+ module UrlHelper
4
+
5
+ # Returns the URL for the set of +options+ provided. This takes the
6
+ # same options as url_for in action controller. For a list, see the
7
+ # documentation for ActionController::Base#url_for. Note that it'll
8
+ # set :only_path => true so you'll get the relative /controller/action
9
+ # instead of the fully qualified http://example.com/controller/action.
10
+ #
11
+ # When called from a view, url_for returns an HTML escaped url. If you
12
+ # need an unescaped url, pass :escape => false in the +options+.
13
+ def url_for(options = {}, *parameters_for_method_reference)
14
+ if options.kind_of? Hash
15
+ options = { :only_path => true }.update(options.symbolize_keys)
16
+ escape = options.key?(:escape) ? options.delete(:escape) : true
17
+ else
18
+ escape = true
19
+ end
20
+
21
+ url = options[:url] #@controller.send(:url_for, options, *parameters_for_method_reference)
22
+ escape ? html_escape(url) : url
23
+ end
24
+
25
+
26
+ # Creates a link tag of the given +name+ using a URL created by the set
27
+ # of +options+. See the valid options in the documentation for
28
+ # ActionController::Base#url_for. It's also possible to pass a string instead
29
+ # of an options hash to get a link tag that uses the value of the string as the
30
+ # href for the link. If nil is passed as a name, the link itself will become
31
+ # the name.
32
+ #
33
+ # The +html_options+ will accept a hash of html attributes for the link tag.
34
+ # It also accepts 3 modifiers that specialize the link behavior.
35
+ #
36
+ # * <tt>:confirm => 'question?'</tt>: This will add a JavaScript confirm
37
+ # prompt with the question specified. If the user accepts, the link is
38
+ # processed normally, otherwise no action is taken.
39
+ # * <tt>:popup => true || array of window options</tt>: This will force the
40
+ # link to open in a popup window. By passing true, a default browser window
41
+ # will be opened with the URL. You can also specify an array of options
42
+ # that are passed-thru to JavaScripts window.open method.
43
+ # * <tt>:method => symbol of HTTP verb</tt>: This modifier will dynamically
44
+ # create an HTML form and immediately submit the form for processing using
45
+ # the HTTP verb specified. Useful for having links perform a POST operation
46
+ # in dangerous actions like deleting a record (which search bots can follow
47
+ # while spidering your site). Supported verbs are :post, :delete and :put.
48
+ # Note that if the user has JavaScript disabled, the request will fall back
49
+ # to using GET. If you are relying on the POST behavior, your should check
50
+ # for it in your controllers action by using the request objects methods
51
+ # for post?, delete? or put?.
52
+ #
53
+ # You can mix and match the +html_options+ with the exception of
54
+ # :popup and :method which will raise an ActionView::ActionViewError
55
+ # exception.
56
+ #
57
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
58
+ # link_to "Help", { :action => "help" }, :popup => true
59
+ # link_to "View Image", { :action => "view" }, :popup => ['new_window_name', 'height=300,width=600']
60
+ # link_to "Delete Image", { :action => "delete", :id => @image.id }, :confirm => "Are you sure?", :method => :delete
61
+ def link_to(name, options = {}, html_options = nil, *parameters_for_method_reference)
62
+ if html_options
63
+ html_options = html_options.stringify_keys
64
+ # convert_options_to_javascript!(html_options)
65
+ tag_options = tag_options(html_options)
66
+ else
67
+ tag_options = nil
68
+ end
69
+
70
+ url = options.is_a?(String) ? options : self.url_for(options, *parameters_for_method_reference)
71
+ "<a href=\"#{url}\"#{tag_options}>#{name || url}</a>"
72
+ end
73
+
74
+
75
+ end
76
+ end
77
+ end
@@ -1,23 +1,25 @@
1
1
  module Waves
2
2
  module Layers
3
+
4
+ # Configures Waves to use the templates in app/templates/errors for exception handling
3
5
  module DefaultErrors
4
6
 
5
7
  def self.included( app )
6
8
 
7
- app.instance_eval do
8
-
9
- autoinit 'Configurations::Mapping' do
9
+ app.auto_eval :Configurations do
10
+ auto_eval :Mapping do
11
+ extend Waves::Mapping
10
12
  handle(Waves::Dispatchers::NotFoundError) do
11
13
  html = Waves.application.views[:errors].process( request ) do
12
14
  not_found_404( :error => Waves::Dispatchers::NotFoundError )
13
15
  end
14
16
  response.status = '404'
15
17
  response.content_type = 'text/html'
16
- response.write( html )
18
+ response.body = html
17
19
  end
18
20
  end
19
-
20
21
  end
22
+
21
23
  end
22
24
  end
23
25
  end
data/lib/layers/mvc.rb ADDED
@@ -0,0 +1,54 @@
1
+ module Waves
2
+ module Layers
3
+ # The MVC layer establishes the Models, Views, Controllers, and Helpers namespaces inside
4
+ # a Waves application. In each namespace, undefined constants are handled by AutoCode, which
5
+ # loads the constant from the correct file in the appropriate directory if it exists, or creates
6
+ # from default otherwise.
7
+ module MVC
8
+
9
+ def self.included( app )
10
+
11
+ def app.models ; self::Models ; end
12
+ def app.views ; self::Views ; end
13
+ def app.controllers ; self::Controllers ; end
14
+ def app.helpers ; self::Helpers ; end
15
+
16
+ app.auto_create_module( :Models ) do
17
+ include AutoCode
18
+ auto_create_class :Default
19
+ auto_load :Default, :directories => [:models]
20
+ end
21
+
22
+ app.auto_eval( :Models ) do
23
+ auto_create_class true, app::Models::Default
24
+ auto_load true, :directories => [ :models ]
25
+ end
26
+
27
+ app.auto_create_module( :Views ) { include AutoCode }
28
+
29
+ app.auto_eval( :Views ) do
30
+ auto_create_class :Default, Waves::Views::Base
31
+ auto_load :Default, :directories => [:views]
32
+ auto_create_class true, app::Views::Default
33
+ auto_load true, :directories => [ :views ]
34
+ end
35
+
36
+ app.auto_create_module( :Controllers ) { include AutoCode }
37
+
38
+ app.auto_eval( :Controllers ) do
39
+ auto_create_class :Default, Waves::Controllers::Base
40
+ auto_load :Default, :directories => [:controllers]
41
+ auto_create_class true, app::Controllers::Default
42
+ auto_load true, :directories => [ :controllers ]
43
+ end
44
+
45
+ app.auto_create_module( :Helpers ) do
46
+ include AutoCode
47
+ auto_create_module { include Waves::Helpers::Default }
48
+ auto_load true, :directories => [ :helpers ]
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,93 @@
1
+ class Symbol
2
+ # Protect ActiveRecord from itself by undefining the to_proc method.
3
+ # Don't worry, AR will redefine it.
4
+ alias :extensions_to_proc :to_proc
5
+ remove_method :to_proc
6
+ end
7
+ require 'active_record'
8
+ require "#{File.dirname(__FILE__)}/active_record/tasks/schema" if defined?(Rake)
9
+ require "#{File.dirname(__FILE__)}/active_record/tasks/generate" if defined?(Rake)
10
+
11
+
12
+ module Waves
13
+ module Layers
14
+ module ORM
15
+
16
+ # Sets up the ActiveRecord connection and configures AutoCode on Models, so that constants in that
17
+ # namespace get loaded from file or created as subclasses of Models::Default
18
+ module ActiveRecord
19
+
20
+ # On inclusion, this module:
21
+ # - creates on the application module a database method that establishes and returns the ActiveRecord connection
22
+ # - arranges for autoloading/autocreation of missing constants in the Models namespace
23
+ # - defines ActiveRecord-specific helper methods on Waves::Controllers::Base
24
+ #
25
+ # The controller helper methdods are:
26
+ # - all
27
+ # - find(name)
28
+ # - create
29
+ # - delete(name)
30
+ # - update(name)
31
+
32
+ def self.included(app)
33
+
34
+ def app.database
35
+ unless @database
36
+ ::ActiveRecord::Base.establish_connection(config.database)
37
+ @database = ::ActiveRecord::Base.connection
38
+ end
39
+ @database
40
+ end
41
+
42
+ app.auto_create_module( :Models ) do
43
+ include AutoCode
44
+ auto_create_class :Default, ::ActiveRecord::Base
45
+ auto_load :Default, :directories => [ :models ]
46
+ end
47
+
48
+ app.auto_eval :Models do
49
+ auto_create_class true, app::Models::Default
50
+ auto_load true, :directories => [ :models ]
51
+
52
+ auto_eval true do
53
+ app.database
54
+ set_table_name basename.snake_case.pluralize.intern
55
+ end
56
+ end
57
+
58
+ Waves::Controllers::Base.instance_eval do
59
+ include Waves::Layers::ORM::ActiveRecord::ControllerMethods
60
+ end
61
+
62
+ end
63
+
64
+ # Mixed into Waves::Controllers::Base. Provides ORM-specific helper methods for model access.
65
+ module ControllerMethods
66
+ def all
67
+ model.find(:all)
68
+ end
69
+
70
+ def find( id )
71
+ model.find(id) or not_found
72
+ end
73
+
74
+ def create
75
+ model.create( attributes )
76
+ end
77
+
78
+ def delete( id )
79
+ find( id ).destroy
80
+ end
81
+
82
+ def update( id )
83
+ instance = find( id )
84
+ instance.update_attributes( attributes )
85
+ instance
86
+ end
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+ end
93
+