make_resourceful_rails2 1.0.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 (39) hide show
  1. data/Rakefile +31 -0
  2. data/VERSION +1 -0
  3. data/lib/make_resourceful.rb +2 -0
  4. data/lib/resourceful/base.rb +63 -0
  5. data/lib/resourceful/builder.rb +405 -0
  6. data/lib/resourceful/default/accessors.rb +418 -0
  7. data/lib/resourceful/default/actions.rb +101 -0
  8. data/lib/resourceful/default/callbacks.rb +51 -0
  9. data/lib/resourceful/default/responses.rb +118 -0
  10. data/lib/resourceful/default/urls.rb +136 -0
  11. data/lib/resourceful/generators/resourceful_scaffold/resourceful_scaffold_generator.rb +87 -0
  12. data/lib/resourceful/generators/resourceful_scaffold/templates/controller.rb +5 -0
  13. data/lib/resourceful/generators/resourceful_scaffold/templates/fixtures.yml +10 -0
  14. data/lib/resourceful/generators/resourceful_scaffold/templates/functional_test.rb +50 -0
  15. data/lib/resourceful/generators/resourceful_scaffold/templates/helper.rb +2 -0
  16. data/lib/resourceful/generators/resourceful_scaffold/templates/migration.rb +13 -0
  17. data/lib/resourceful/generators/resourceful_scaffold/templates/model.rb +2 -0
  18. data/lib/resourceful/generators/resourceful_scaffold/templates/unit_test.rb +7 -0
  19. data/lib/resourceful/generators/resourceful_scaffold/templates/view__form.haml +5 -0
  20. data/lib/resourceful/generators/resourceful_scaffold/templates/view_edit.haml +11 -0
  21. data/lib/resourceful/generators/resourceful_scaffold/templates/view_index.haml +5 -0
  22. data/lib/resourceful/generators/resourceful_scaffold/templates/view_new.haml +9 -0
  23. data/lib/resourceful/generators/resourceful_scaffold/templates/view_partial.haml +12 -0
  24. data/lib/resourceful/generators/resourceful_scaffold/templates/view_show.haml +14 -0
  25. data/lib/resourceful/maker.rb +92 -0
  26. data/lib/resourceful/response.rb +33 -0
  27. data/lib/resourceful/serialize.rb +185 -0
  28. data/spec/accessors_spec.rb +474 -0
  29. data/spec/actions_spec.rb +310 -0
  30. data/spec/base_spec.rb +12 -0
  31. data/spec/builder_spec.rb +332 -0
  32. data/spec/callbacks_spec.rb +71 -0
  33. data/spec/integration_spec.rb +394 -0
  34. data/spec/maker_spec.rb +91 -0
  35. data/spec/response_spec.rb +37 -0
  36. data/spec/responses_spec.rb +314 -0
  37. data/spec/serialize_spec.rb +133 -0
  38. data/spec/urls_spec.rb +282 -0
  39. metadata +114 -0
@@ -0,0 +1,51 @@
1
+ require 'resourceful/builder'
2
+
3
+ module Resourceful
4
+ module Default
5
+ # This module is mostly meant to be used by the make_resourceful default actions.
6
+ # It provides various methods that declare where callbacks set in the +make_resourceful+ block,
7
+ # like Builder#before and Builder#response_for,
8
+ # should be called.
9
+ module Callbacks
10
+ # Calls any +before+ callbacks set in the +make_resourceful+ block for the given event.
11
+ def before(event)
12
+ resourceful_fire(:before, event.to_sym)
13
+ end
14
+
15
+ # Calls any +after+ callbacks set in the +make_resourceful+ block for the given event.
16
+ def after(event)
17
+ resourceful_fire(:after, event.to_sym)
18
+ end
19
+
20
+ # Calls any +response_for+ callbacks set in the +make_resourceful+ block for the given event.
21
+ # Note that these aren't called directly,
22
+ # but instead passed along to Rails' respond_to method.
23
+ def response_for(event)
24
+ if responses = self.class.resourceful_responses[event.to_sym]
25
+ respond_to do |format|
26
+ responses.each do |key, value|
27
+ format.send(key, &scope(value))
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ # Returns a block identical to the given block,
34
+ # but in the context of the current controller.
35
+ # The returned block accepts no arguments,
36
+ # even if the given block accepted them.
37
+ def scope(block)
38
+ proc do
39
+ instance_eval(&(block || proc {}))
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def resourceful_fire(type, name)
46
+ callbacks = self.class.resourceful_callbacks[type][name] || []
47
+ callbacks.each { |callback| scope(callback).call }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,118 @@
1
+ module Resourceful
2
+ module Default
3
+ module Responses
4
+ # Sets the default flash message.
5
+ # This message can be overridden by passing in
6
+ # an HTTP parameter of the form "_flash[type]" via POST or GET.
7
+ #
8
+ # You can use this to easily have multiple forms
9
+ # post to the same create/edit/destroy actions
10
+ # but display different flash notices -
11
+ # without modifying the controller code at all.
12
+ #
13
+ # By default, the flash types are +notice+ when the database operation completes successfully
14
+ # and +error+ when it fails.
15
+ #
16
+ #--
17
+ # TODO: Move this out of here
18
+ #++
19
+ def set_default_flash(type, message)
20
+ flash[type] ||= (params[:_flash] && params[:_flash][type]) || message
21
+ end
22
+
23
+ # Sets the default redirect
24
+ # (the argument passed to +redirect_to+).
25
+ # This message can be overridden by passing in
26
+ # an HTTP parameter of the form "_redirect_on[status]" via POST or GET.
27
+ #
28
+ # You can use this to easily have multiple forms
29
+ # post to the same create/edit/destroy actions
30
+ # but redirect to different URLs -
31
+ # without modifying the controller code at all.
32
+ #
33
+ # By default, the redirect statuses are +success+ when the database operation completes successfully
34
+ # and +failure+ when it fails.
35
+ # Use the <tt>:status</tt> option to specify which status to run the redirect for.
36
+ # For example:
37
+ #
38
+ # set_default_redirect "/posts", :status => :failure
39
+ #
40
+ # This will run <tt>redirect_to params[:_redirect_on][:failure]</tt> if the parameter exists,
41
+ # or <tt>redirect_to "/posts"</tt> otherwise.
42
+ #
43
+ #--
44
+ # TODO: Move this out of here
45
+ #++
46
+ def set_default_redirect(to, options = {})
47
+ status = options[:status] || :success
48
+ redirect_to (params[:_redirect_on] && params[:_redirect_on][status]) || to
49
+ end
50
+
51
+ # This method is automatically run when this module is included in Resourceful::Base.
52
+ # It sets up the default responses for the default actions.
53
+ def self.included(base)
54
+ base.made_resourceful do
55
+ response_for(:show, :index, :edit, :new) do |format|
56
+ format.html
57
+ format.js
58
+ end
59
+
60
+ response_for(:show_fails) do |format|
61
+ not_found = Proc.new { render :text => I18n.t('make_resourceful.show.fails', :default => "No item found"), :status => 404 }
62
+ format.html &not_found
63
+ format.js &not_found
64
+ format.xml &not_found
65
+ end
66
+
67
+ response_for(:create) do |format|
68
+ format.html do
69
+ set_default_flash :notice, I18n.t('make_resourceful.create.success', :default => "Create successful!")
70
+ set_default_redirect object_path
71
+ end
72
+ format.js
73
+ end
74
+
75
+ response_for(:create_fails) do |format|
76
+ format.html do
77
+ set_default_flash :error, I18n.t('make_resourceful.create.fails', :default => "There was a problem!")
78
+ render :action => :new, :status => 422
79
+ end
80
+ format.js
81
+ end
82
+
83
+ response_for(:update) do |format|
84
+ format.html do
85
+ set_default_flash :notice, I18n.t('make_resourceful.update.success', :default => "Save successful!")
86
+ set_default_redirect object_path
87
+ end
88
+ format.js
89
+ end
90
+
91
+ response_for(:update_fails) do |format|
92
+ format.html do
93
+ set_default_flash :error, I18n.t('make_resourceful.update.fails', :default => "There was a problem saving!")
94
+ render :action => :edit, :status => 422
95
+ end
96
+ format.js
97
+ end
98
+
99
+ response_for(:destroy) do |format|
100
+ format.html do
101
+ set_default_flash :notice, I18n.t('make_resourceful.destroy.success', :default => "Record deleted!")
102
+ set_default_redirect objects_path
103
+ end
104
+ format.js
105
+ end
106
+
107
+ response_for(:destroy_fails) do |format|
108
+ format.html do
109
+ set_default_flash :error, I18n.t('make_resourceful.destroy.fails', :default => "There was a problem deleting!")
110
+ set_default_redirect :back, :status => :failure
111
+ end
112
+ format.js
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,136 @@
1
+ module Resourceful
2
+ module Default
3
+ # This file contains various methods to make URL helpers less painful.
4
+ # They provide methods analogous to the standard foo_url and foo_path helpers.
5
+ # However, they use make_resourceful's knowledge of the structure of the controller
6
+ # to allow you to avoid figuring out which method to call and which parent objects it should be passed.
7
+ module URLs
8
+ # This returns the path for the given object,
9
+ # by default current_object[link:classes/Resourceful/Default/Accessors.html#M000012].
10
+ # For example, in HatsController the following are equivalent:
11
+ #
12
+ # object_path #=> "/hats/12"
13
+ # hat_path(@hat) #=> "/hats/12"
14
+ #
15
+ def object_path(object = current_object); object_route(object, 'path'); end
16
+ # Same as object_path, but with the protocol and hostname.
17
+ def object_url (object = current_object); object_route(object, 'url'); end
18
+
19
+ # This is the same as object_path,
20
+ # unless a parent exists.
21
+ # Then it returns the nested path for the object.
22
+ # For example, in HatsController where Person has_many :hats and <tt>params[:person_id] == 42</tt>,
23
+ # the following are equivalent:
24
+ #
25
+ # nested_object_path #=> "/person/42/hats/12"
26
+ # person_hat_path(@person, @hat) #=> "/person/42/hats/12"
27
+ #
28
+ def nested_object_path(object = current_object); nested_object_route(object, 'path'); end
29
+ # Same as nested_object_path, but with the protocol and hostname.
30
+ def nested_object_url (object = current_object); nested_object_route(object, 'url'); end
31
+
32
+ # This returns the path for the edit action for the given object,
33
+ # by default current_object[link:classes/Resourceful/Default/Accessors.html#M000012].
34
+ # For example, in HatsController the following are equivalent:
35
+ #
36
+ # edit_object_path #=> "/hats/12/edit"
37
+ # edit_person_hat_path(@person, @hat) #=> "/hats/12/edit"
38
+ #
39
+ def edit_object_path(object = current_object); edit_object_route(object, 'path'); end
40
+ # Same as edit_object_path, but with the protocol and hostname.
41
+ def edit_object_url (object = current_object); edit_object_route(object, 'url'); end
42
+
43
+ # This returns the path for the collection of the current controller.
44
+ # For example, in HatsController where Person has_many :hats and <tt>params[:person_id] == 42</tt>,
45
+ # the following are equivalent:
46
+ #
47
+ # objects_path #=> "/people/42/hats"
48
+ # person_hats_path(@person) #=> "/people/42/hats"
49
+ #
50
+ def objects_path; objects_route('path'); end
51
+ # Same as objects_path, but with the protocol and hostname.
52
+ def objects_url ; objects_route('url'); end
53
+
54
+ # This returns the path for the new action for the current controller.
55
+ # For example, in HatsController where Person has_many :hats and <tt>params[:person_id] == 42</tt>,
56
+ # the following are equivalent:
57
+ #
58
+ # new_object_path #=> "/people/42/hats/new"
59
+ # new_person_hat_path(@person) #=> "/people/42/hats/new"
60
+ #
61
+ def new_object_path; new_object_route('path'); end
62
+ # Same as new_object_path, but with the protocol and hostname.
63
+ def new_object_url ; new_object_route('url'); end
64
+
65
+ # This returns the path for the parent object.
66
+ #
67
+ def parent_path(object = parent_object)
68
+ instance_route(parent_class_name.underscore, object, 'path')
69
+ end
70
+ # Same as parent_path, but with the protocol and hostname.
71
+ def parent_url(object = parent_object)
72
+ instance_route(parent_class_name.underscore, object, 'url')
73
+ end
74
+
75
+ # This prefix is added to the Rails URL helper names
76
+ # before they're called.
77
+ # By default, it's the underscored list of namespaces of the current controller,
78
+ # or nil if there are no namespaces defined.
79
+ # However, it can be overridden if another prefix is needed.
80
+ # Note that if this is overridden,
81
+ # the new method should return a string ending in an underscore.
82
+ #
83
+ # For example, in Admin::Content::PagesController:
84
+ #
85
+ # url_helper_prefix #=> "admin_content_"
86
+ #
87
+ # Then object_path is the same as <tt>admin_content_page_path(current_object)</tt>.
88
+ def url_helper_prefix
89
+ namespaces.empty? ? nil : "#{namespaces.join('_')}_"
90
+ end
91
+
92
+ # This prefix is added to the Rails URL helper names
93
+ # for the make_resourceful collection URL helpers,
94
+ # objects_path and new_object_path.
95
+ # By default, it's the parent name followed by an underscore if a parent
96
+ # is given, and the empty string otherwise.
97
+ #
98
+ # See also url_helper_prefix.
99
+ def collection_url_prefix
100
+ parent? ? "#{parent_class_name.underscore}_" : ''
101
+ end
102
+
103
+ private
104
+
105
+ def object_route(object, type)
106
+ instance_route(current_model_name.underscore, object, type)
107
+ end
108
+
109
+ def nested_object_route(object, type)
110
+ return object_route(object, type) unless parent?
111
+ send("#{url_helper_prefix}#{parent_class_name.underscore}_#{current_model_name.underscore}_#{type}", parent_object, object)
112
+ end
113
+
114
+ def edit_object_route(object, type)
115
+ instance_route(current_model_name.underscore, object, type, "edit")
116
+ end
117
+
118
+ def objects_route(type)
119
+ collection_route(current_model_name.pluralize.underscore, type)
120
+ end
121
+
122
+ def new_object_route(type)
123
+ collection_route(current_model_name.underscore, type, "new")
124
+ end
125
+
126
+ def instance_route(name, object, type, action = nil)
127
+ send("#{action ? action + '_' : ''}#{url_helper_prefix}#{collection_url_prefix unless shallow?}#{name}_#{type}", *(parent? && !shallow? ? [parent_object, object] : [object]))
128
+ end
129
+
130
+ def collection_route(name, type, action = nil)
131
+ send("#{action ? action + '_' : ''}#{url_helper_prefix}#{collection_url_prefix}#{name}_#{type}",
132
+ *(parent? ? [parent_object] : []))
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,87 @@
1
+ class ResourcefulScaffoldGenerator < Rails::Generators::Base
2
+ attr_reader :controller_class_path,
3
+ :controller_file_path,
4
+ :controller_class_nesting,
5
+ :controller_class_nesting_depth,
6
+ :controller_class_name,
7
+ :controller_underscore_name,
8
+ :controller_plural_name
9
+ alias_method :controller_file_name, :controller_underscore_name
10
+ alias_method :controller_table_name, :controller_plural_name
11
+
12
+ def initialize(runtime_args, runtime_options = {})
13
+ super
14
+
15
+ base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@name.pluralize)
16
+ @controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
17
+
18
+ if @controller_class_nesting.empty?
19
+ @controller_class_name = @controller_class_name_without_nesting
20
+ else
21
+ @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
22
+ end
23
+ end
24
+
25
+ def manifest
26
+ record do |m|
27
+ # Check for class naming collisions.
28
+ m.class_collisions(controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper")
29
+ m.class_collisions(class_path, "#{class_name}")
30
+
31
+ # Controller, helper, views, and test directories.
32
+ m.directory(File.join('app/models', class_path))
33
+ m.directory(File.join('app/controllers', controller_class_path))
34
+ m.directory(File.join('app/helpers', controller_class_path))
35
+ m.directory(File.join('app/views', controller_class_path, controller_file_name))
36
+ m.directory(File.join('test/functional', controller_class_path))
37
+ m.directory(File.join('test/unit', class_path))
38
+ m.directory(File.join('test/fixtures', class_path))
39
+
40
+ # Views
41
+ for action in scaffold_views
42
+ m.template("view_#{action}.haml", File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.haml"))
43
+ end
44
+ m.template('view_partial.haml', File.join('app/views', controller_class_path, controller_file_name, "_#{singular_name}.html.haml"))
45
+
46
+ # Helper
47
+ m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
48
+
49
+ # Model
50
+ m.template('model.rb', File.join('app/models', class_path, "#{file_name}.rb"))
51
+
52
+ unless options[:skip_migration]
53
+ m.migration_template('migration.rb', 'db/migrate',
54
+ :assigns => {
55
+ :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}",
56
+ :attributes => attributes
57
+ },
58
+ :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}")
59
+ end
60
+
61
+ # Controller
62
+ m.template('controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb"))
63
+
64
+ # Tests
65
+ m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
66
+ m.template('unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb"))
67
+ m.template('fixtures.yml', File.join('test/fixtures', "#{table_name}.yml"))
68
+
69
+ # Route
70
+ m.route_resources controller_file_name
71
+ end
72
+ end
73
+
74
+ protected
75
+
76
+ def banner
77
+ "Usage: #{$0} resourcefulscaffold ModelName [field:type, field:type]"
78
+ end
79
+
80
+ def scaffold_views
81
+ %w[ index show new edit _form ]
82
+ end
83
+
84
+ def model_name
85
+ class_name.demodulize
86
+ end
87
+ end
@@ -0,0 +1,5 @@
1
+ class <%= controller_class_name %>Controller < ApplicationController
2
+ make_resourceful do
3
+ actions :all
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ one:
2
+ id: 1
3
+ <% for attribute in attributes -%>
4
+ <%= attribute.name %>: <%= attribute.default %>
5
+ <% end -%>
6
+ two:
7
+ id: 2
8
+ <% for attribute in attributes -%>
9
+ <%= attribute.name %>: <%= attribute.default %>
10
+ <% end -%>
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../test_helper'
2
+ require '<%= controller_file_path %>_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
6
+
7
+ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
8
+
9
+ def test_should_get_index
10
+ get :index
11
+ assert_response :success
12
+ assert assigns(:<%= table_name %>)
13
+ end
14
+
15
+ def test_should_get_new
16
+ get :new
17
+ assert_response :success
18
+ end
19
+
20
+ def test_should_create_<%= file_name %>
21
+ old_count = <%= class_name %>.count
22
+ post :create, :<%= file_name %> => { }
23
+ assert_equal old_count + 1, <%= class_name %>.count
24
+
25
+ assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
26
+ end
27
+
28
+ def test_should_show_<%= file_name %>
29
+ get :show, :id => 1
30
+ assert_response :success
31
+ end
32
+
33
+ def test_should_get_edit
34
+ get :edit, :id => 1
35
+ assert_response :success
36
+ end
37
+
38
+ def test_should_update_<%= file_name %>
39
+ put :update, :id => 1, :<%= file_name %> => { }
40
+ assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
41
+ end
42
+
43
+ def test_should_destroy_<%= file_name %>
44
+ old_count = <%= class_name %>.count
45
+ delete :destroy, :id => 1
46
+ assert_equal old_count-1, <%= class_name %>.count
47
+
48
+ assert_redirected_to <%= table_name %>_path
49
+ end
50
+ end