controller_resources 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +7 -0
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +6 -3
  6. data/README.md +107 -13
  7. data/Rakefile +15 -1
  8. data/bin/cc-tddium-post-worker +16 -0
  9. data/bin/erubis +16 -0
  10. data/bin/htmldiff +16 -0
  11. data/bin/ldiff +16 -0
  12. data/bin/nokogiri +16 -0
  13. data/bin/rackup +16 -0
  14. data/bin/rails +12 -8
  15. data/bin/rake +16 -0
  16. data/bin/rspec +16 -0
  17. data/bin/rubocop +16 -0
  18. data/bin/ruby-parse +16 -0
  19. data/bin/ruby-rewrite +16 -0
  20. data/bin/sprockets +16 -0
  21. data/bin/thor +16 -0
  22. data/controller_resources.gemspec +14 -6
  23. data/lib/controller_resources/engine.rb +1 -0
  24. data/lib/controller_resources/extension.rb +65 -63
  25. data/lib/controller_resources/resource.rb +99 -0
  26. data/lib/controller_resources/version.rb +2 -1
  27. data/lib/controller_resources.rb +13 -4
  28. data/spec/dummy/app/assets/javascripts/posts.js +2 -0
  29. data/spec/dummy/app/assets/stylesheets/posts.css +4 -0
  30. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  31. data/spec/dummy/app/controllers/posts_controller.rb +44 -0
  32. data/spec/dummy/app/helpers/posts_helper.rb +2 -0
  33. data/spec/dummy/app/models/post.rb +2 -0
  34. data/spec/dummy/app/views/posts/_form.html.erb +29 -0
  35. data/spec/dummy/app/views/posts/edit.html.erb +6 -0
  36. data/spec/dummy/app/views/posts/index.html.erb +31 -0
  37. data/spec/dummy/app/views/posts/new.html.erb +5 -0
  38. data/spec/dummy/app/views/posts/show.html.erb +19 -0
  39. data/spec/dummy/config/application.rb +2 -0
  40. data/spec/dummy/config/environments/test.rb +1 -1
  41. data/spec/dummy/config/routes.rb +1 -2
  42. data/spec/dummy/db/migrate/20150614211054_create_posts.rb +10 -0
  43. data/spec/dummy/db/schema.rb +26 -0
  44. data/spec/dummy/test/controllers/posts_controller_test.rb +49 -0
  45. data/spec/dummy/test/fixtures/posts.yml +11 -0
  46. data/spec/dummy/test/models/post_test.rb +7 -0
  47. data/spec/integration/navigation_spec.rb +1 -1
  48. data/spec/lib/controller_resources/extension_spec.rb +42 -0
  49. data/spec/lib/controller_resources/resource_spec.rb +46 -0
  50. data/spec/spec_helper.rb +16 -14
  51. metadata +98 -26
  52. data/lib/controller_resources/responder.rb +0 -0
  53. data/lib/generators/controller_resources/USAGE +0 -8
  54. data/lib/generators/controller_resources/install_generator.rb +0 -16
  55. data/spec/controller_resources/extension_spec.rb +0 -32
  56. data/spec/generators/controller_resources/install_generator_spec.rb +0 -29
@@ -1,6 +1,3 @@
1
- require 'decent_exposure'
2
- require 'responders'
3
-
4
1
  module ControllerResources
5
2
  # A single macro that combines all controller-level macros we use for
6
3
  # the front-end of this application. Simply use the
@@ -15,19 +12,17 @@ module ControllerResources
15
12
  # def index
16
13
  # respond_with artists
17
14
  # end
15
+ #
16
+ # def show
17
+ # respond_with artist
18
+ # end
18
19
  # end
19
20
  #
20
21
  module Extension
21
22
  extend ActiveSupport::Concern
22
23
 
23
24
  included do
24
- # Attributes for configuring ControllerResources.
25
- class_attribute :_search_params
26
- class_attribute :_edit_params
27
- class_attribute :_singleton_resource
28
- class_attribute :_collection_resource
29
- class_attribute :_finder_method
30
- class_attribute :_protected_actions
25
+ class_attribute :_resource
31
26
 
32
27
  # Use the StrongParameters strategy in DecentExposure
33
28
  decent_configuration do
@@ -39,71 +34,78 @@ module ControllerResources
39
34
  # Use the FlashResponder and HttpCacheResponder to respond to
40
35
  # various requests.
41
36
  responders :flash, :http_cache
37
+
38
+ helper_method :current_resource
42
39
  end
43
40
 
41
+ # Macros included into the controller as class methods.
44
42
  module ClassMethods
45
- # Initialize this controller as an authenticated resource.
46
- def resource(name=nil, &block)
47
- name = self.name.gsub(/Controller/, '').tableize if name.nil?
48
- self._singleton_resource = name.to_s.singularize.to_sym
49
- self._collection_resource = name.to_s.pluralize.to_sym
50
- self._protected_actions = %w(create update destroy edit new)
51
-
52
- class_eval <<-RUBY
53
- expose :#{model}, except: %w(index)
54
- expose :#{collection}, only: %w(index) do
55
- #{model_class}.where(search_params)
56
- end
57
- #{authenticate if defined? Devise}
58
- #{authorize if defined? Authority}
59
- RUBY
60
-
61
- yield if block_given?
62
- end
63
-
64
- protected
65
- # Set the search params for this controller.
66
- def search(*hash_of_params)
67
- self._search_params = hash_of_params
68
- end
69
-
70
- # Set the edit params for this controller.
71
- def modify(*hash_of_params)
72
- self._edit_params = hash_of_params
73
- end
74
-
75
- def protect(action)
76
- self._protected_actions << action
77
- end
78
-
79
- private
80
- def authenticate
81
- "before_action :authenticate_user!, only: %w(#{_protected_actions.join(' ')})"
82
- end
83
-
84
- def authorize
85
- "authorize_actions_for #{model.to_s.classify}, only: %w(#{_protected_actions.join(' ')})"
86
- end
87
-
88
- def model_class
89
- @model_class ||= self._singleton_resource.to_s.classify
43
+ # Initialize this controller as an authenticated resource. You can
44
+ # optionally specify search_params and edit_params which are
45
+ # formed into strong parameter hashes.
46
+ #
47
+ # Example:
48
+ #
49
+ # resource :post do
50
+ # search :title
51
+ # modify :title, :body
52
+ # end
53
+ #
54
+ # @param [String] name - The parameterized name of the model
55
+ # backing this controller.
56
+ def resource(name = self.name.gsub(/Controller/, '').tableize, &block)
57
+ self._resource = Resource.new(name, &block)
58
+
59
+ expose(
60
+ _resource.model_name,
61
+ except: %i(index)
62
+ )
63
+ expose(
64
+ _resource.collection_name,
65
+ only: %i(index),
66
+ attributes: :search_params
67
+ )
90
68
  end
69
+ end
91
70
 
92
- def model
93
- self._singleton_resource
71
+ # Omit any unpermitted parameters when searching in collections.
72
+ # Permits all parameters when none are given in the resource block.
73
+ #
74
+ # @returns [StrongParameters::Hash]
75
+ def search_params
76
+ if resource.search_params.any?
77
+ params.permit resource.search_params
78
+ else
79
+ params.permit!
94
80
  end
81
+ end
95
82
 
96
- def collection
97
- @collection ||= self._collection_resource.to_s
83
+ # Omit any unpermitted parameters when editing or creating content.
84
+ # Permits all parameters when none are given in the resource block.
85
+ #
86
+ # @returns [StrongParameters::Hash]
87
+ def edit_params
88
+ if resource.edit_params.any?
89
+ params.require(resource.model_name).permit resource.edit_params
90
+ else
91
+ params.require(resource.model_name).permit!
98
92
  end
99
93
  end
100
94
 
101
- def search_params
102
- params.permit self.class._search_params
95
+ # The resource object as made available to the controller
96
+ #
97
+ # @returns [Resource]
98
+ def resource
99
+ fail Resource::NotConfiguredError unless self.class._resource.present?
100
+ self.class._resource
103
101
  end
104
102
 
105
- def edit_params
106
- params.require(self.class._singleton_resource).permit self._edit_params
103
+ # A helper for engines like Pundit to configure the current
104
+ # authorizer policy for this controller.
105
+ #
106
+ # @returns [Object] the class name of the configured model resource.
107
+ def current_resource
108
+ resource.model_class
107
109
  end
108
110
  end
109
111
  end
@@ -0,0 +1,99 @@
1
+ module ControllerResources
2
+ # The "data model" for the controlled resource. In the controller,
3
+ # defining the +resource+ block instantiates this class, which is used
4
+ # to hold all of the data the developer provides inside the block and
5
+ # used in the +Extension+ to actually do the "heavy lifting" in the
6
+ # controller. This class is just a way of storing all of the
7
+ # configured state for a given controller's resource.
8
+ class Resource
9
+ # The name of this resource, used to look up the collection and
10
+ # model names.
11
+ #
12
+ # @type [String]
13
+ attr_reader :name
14
+
15
+ # Permitted parameters for searching collections.
16
+ #
17
+ # @type [Array]
18
+ attr_reader :search_params
19
+
20
+ # Permitted parameters for editing members.
21
+ #
22
+ # @type [Array]
23
+ attr_reader :edit_params
24
+
25
+ # Thrown when a Resource object has not been configured, but we are
26
+ # attempting to use methods defined by ControllerResources.
27
+ class NotConfiguredError < StandardError; end
28
+
29
+ # Instantiate a new Resource with the given name and a block to
30
+ # define permitted parameters. The code given in the +resource+
31
+ # block is passed directly here so that the Resource class can be
32
+ # set up with its proper state.
33
+ #
34
+ # By default, resources are not given any search or edit params. If
35
+ # none are provided (via the +search+ and +modify+ configuration
36
+ # methods), all parameters will be permitted on collection and
37
+ # member resources respectively.
38
+ #
39
+ # @param [Symbol] name - The name of this resource, typically given
40
+ # as a singular symbol.
41
+ #
42
+ # @param [Block] &block - A block of code used to set up this
43
+ # object.
44
+ def initialize(name, &block)
45
+ @name = name.to_s
46
+ @search_params = []
47
+ @edit_params = []
48
+ @block = block
49
+ yield self if block_given?
50
+ end
51
+
52
+ # Singular version of the given resource name.
53
+ #
54
+ # @returns [Symbol]
55
+ def model_name
56
+ name.singularize.to_sym
57
+ end
58
+
59
+ # Pluralized version of the given resource name.
60
+ #
61
+ # @returns [Symbol]
62
+ def collection_name
63
+ name.pluralize.to_sym
64
+ end
65
+
66
+ # The model name as a class constant.
67
+ #
68
+ # @returns [Object]
69
+ def model_class
70
+ @model_class ||= model_name.to_s.classify.constantize
71
+ end
72
+
73
+ # Set the search params for this controller.
74
+ #
75
+ # Example:
76
+ #
77
+ # resource :post do
78
+ # search :title, :category, :tag
79
+ # end
80
+ #
81
+ # @param [Array] A collection of params to whitelist.
82
+ def search(*params)
83
+ @search_params = params
84
+ end
85
+
86
+ # Set the edit params for this controller.
87
+ #
88
+ # Example:
89
+ #
90
+ # resource :post do
91
+ # modify :title, :category, :tag, :body, :is_published
92
+ # end
93
+ #
94
+ # @param [Array] A collection of params to whitelist.
95
+ def modify(*params)
96
+ @edit_params = params
97
+ end
98
+ end
99
+ end
@@ -1,3 +1,4 @@
1
+ # :nodoc:
1
2
  module ControllerResources
2
- VERSION = "0.0.5"
3
+ VERSION = '0.0.6'
3
4
  end
@@ -1,7 +1,16 @@
1
- require "controller_resources/engine"
2
- require "controller_resources/version"
3
- require "controller_resources/extension"
1
+ require 'active_support/all'
2
+ require 'decent_exposure'
3
+ require 'responders'
4
+ require 'controller_resources/version'
4
5
 
6
+ # A controller DSL for Rails that allows you to easily and quickly define
7
+ # both singular and collection model resources that can be operated on
8
+ # within the controller. Attempts to DRY up most of the boilerplate code
9
+ # at the top of each controller used to set up its state.
5
10
  module ControllerResources
6
- # config
11
+ extend ActiveSupport::Autoload
12
+
13
+ autoload :Engine
14
+ autoload :Resource
15
+ autoload :Extension
7
16
  end
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,56 @@
1
+ body { background-color: #fff; color: #333; }
2
+
3
+ body, p, ol, ul, td {
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px;
7
+ }
8
+
9
+ pre {
10
+ background-color: #eee;
11
+ padding: 10px;
12
+ font-size: 11px;
13
+ }
14
+
15
+ a { color: #000; }
16
+ a:visited { color: #666; }
17
+ a:hover { color: #fff; background-color:#000; }
18
+
19
+ div.field, div.actions {
20
+ margin-bottom: 10px;
21
+ }
22
+
23
+ #notice {
24
+ color: green;
25
+ }
26
+
27
+ .field_with_errors {
28
+ padding: 2px;
29
+ background-color: red;
30
+ display: table;
31
+ }
32
+
33
+ #error_explanation {
34
+ width: 450px;
35
+ border: 2px solid red;
36
+ padding: 7px;
37
+ padding-bottom: 0;
38
+ margin-bottom: 20px;
39
+ background-color: #f0f0f0;
40
+ }
41
+
42
+ #error_explanation h2 {
43
+ text-align: left;
44
+ font-weight: bold;
45
+ padding: 5px 5px 5px 15px;
46
+ font-size: 12px;
47
+ margin: -7px;
48
+ margin-bottom: 0px;
49
+ background-color: #c00;
50
+ color: #fff;
51
+ }
52
+
53
+ #error_explanation ul li {
54
+ font-size: 12px;
55
+ list-style: square;
56
+ }
@@ -0,0 +1,44 @@
1
+ class PostsController < ApplicationController
2
+ resource :post do
3
+ search :title
4
+ modify :title, :body
5
+ end
6
+
7
+ # GET /posts
8
+ def index
9
+ respond_with posts
10
+ end
11
+
12
+ # GET /posts/1
13
+ def show
14
+ respond_with post
15
+ end
16
+
17
+ # GET /posts/new
18
+ def new
19
+ render :new
20
+ end
21
+
22
+ # GET /posts/1/edit
23
+ def edit
24
+ render :edit
25
+ end
26
+
27
+ # POST /posts
28
+ def create
29
+ post.save
30
+ respond_with post
31
+ end
32
+
33
+ # PATCH/PUT /posts/1
34
+ def update
35
+ post.update(edit_params)
36
+ respond_with post
37
+ end
38
+
39
+ # DELETE /posts/1
40
+ def destroy
41
+ post.destroy
42
+ respond_with post
43
+ end
44
+ end
@@ -0,0 +1,2 @@
1
+ module PostsHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ class Post < ActiveRecord::Base
2
+ end
@@ -0,0 +1,29 @@
1
+ <%= form_for(@post) do |f| %>
2
+ <% if @post.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
5
+
6
+ <ul>
7
+ <% @post.errors.full_messages.each do |message| %>
8
+ <li><%= message %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div class="field">
15
+ <%= f.label :title %><br>
16
+ <%= f.text_field :title %>
17
+ </div>
18
+ <div class="field">
19
+ <%= f.label :body %><br>
20
+ <%= f.text_field :body %>
21
+ </div>
22
+ <div class="field">
23
+ <%= f.label :is_published %><br>
24
+ <%= f.check_box :is_published %>
25
+ </div>
26
+ <div class="actions">
27
+ <%= f.submit %>
28
+ </div>
29
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <h1>Editing Post</h1>
2
+
3
+ <%= render 'form' %>
4
+
5
+ <%= link_to 'Show', @post %> |
6
+ <%= link_to 'Back', posts_path %>
@@ -0,0 +1,31 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h1>Listing Posts</h1>
4
+
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th>Title</th>
9
+ <th>Body</th>
10
+ <th>Is published</th>
11
+ <th colspan="3"></th>
12
+ </tr>
13
+ </thead>
14
+
15
+ <tbody>
16
+ <% @posts.each do |post| %>
17
+ <tr>
18
+ <td><%= post.title %></td>
19
+ <td><%= post.body %></td>
20
+ <td><%= post.is_published %></td>
21
+ <td><%= link_to 'Show', post %></td>
22
+ <td><%= link_to 'Edit', edit_post_path(post) %></td>
23
+ <td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+ </table>
28
+
29
+ <br>
30
+
31
+ <%= link_to 'New Post', new_post_path %>
@@ -0,0 +1,5 @@
1
+ <h1>New Post</h1>
2
+
3
+ <%= render 'form' %>
4
+
5
+ <%= link_to 'Back', posts_path %>
@@ -0,0 +1,19 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <p>
4
+ <strong>Title:</strong>
5
+ <%= @post.title %>
6
+ </p>
7
+
8
+ <p>
9
+ <strong>Body:</strong>
10
+ <%= @post.body %>
11
+ </p>
12
+
13
+ <p>
14
+ <strong>Is published:</strong>
15
+ <%= @post.is_published %>
16
+ </p>
17
+
18
+ <%= link_to 'Edit', edit_post_path(@post) %> |
19
+ <%= link_to 'Back', posts_path %>
@@ -18,6 +18,8 @@ module Dummy
18
18
  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
19
19
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
20
20
  # config.i18n.default_locale = :de
21
+ #
22
+ config.active_support.test_order = :random
21
23
  end
22
24
  end
23
25
 
@@ -13,7 +13,7 @@ Rails.application.configure do
13
13
  config.eager_load = false
14
14
 
15
15
  # Configure static asset server for tests with Cache-Control for performance.
16
- config.serve_static_assets = true
16
+ config.serve_static_files = true
17
17
  config.static_cache_control = 'public, max-age=3600'
18
18
 
19
19
  # Show full error reports and disable caching.
@@ -1,4 +1,3 @@
1
1
  Rails.application.routes.draw do
2
-
3
- mount ControllerResources::Engine => "/controller_resources"
2
+ resources :posts
4
3
  end
@@ -0,0 +1,10 @@
1
+ class CreatePosts < ActiveRecord::Migration
2
+ def change
3
+ create_table :posts do |t|
4
+ t.string :title
5
+ t.string :body
6
+
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended that you check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(version: 20150614211054) do
15
+
16
+ # These are extensions that must be enabled in order to support this database
17
+ enable_extension "plpgsql"
18
+
19
+ create_table "posts", force: :cascade do |t|
20
+ t.string "title"
21
+ t.string "body"
22
+ t.datetime "created_at", null: false
23
+ t.datetime "updated_at", null: false
24
+ end
25
+
26
+ end
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ class PostsControllerTest < ActionController::TestCase
4
+ setup do
5
+ @post = posts(:one)
6
+ end
7
+
8
+ test "should get index" do
9
+ get :index
10
+ assert_response :success
11
+ assert_not_nil assigns(:posts)
12
+ end
13
+
14
+ test "should get new" do
15
+ get :new
16
+ assert_response :success
17
+ end
18
+
19
+ test "should create post" do
20
+ assert_difference('Post.count') do
21
+ post :create, post: { body: @post.body, is_published: @post.is_published, title: @post.title }
22
+ end
23
+
24
+ assert_redirected_to post_path(assigns(:post))
25
+ end
26
+
27
+ test "should show post" do
28
+ get :show, id: @post
29
+ assert_response :success
30
+ end
31
+
32
+ test "should get edit" do
33
+ get :edit, id: @post
34
+ assert_response :success
35
+ end
36
+
37
+ test "should update post" do
38
+ patch :update, id: @post, post: { body: @post.body, is_published: @post.is_published, title: @post.title }
39
+ assert_redirected_to post_path(assigns(:post))
40
+ end
41
+
42
+ test "should destroy post" do
43
+ assert_difference('Post.count', -1) do
44
+ delete :destroy, id: @post
45
+ end
46
+
47
+ assert_redirected_to posts_path
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
+
3
+ one:
4
+ title: MyString
5
+ body: MyString
6
+ is_published: false
7
+
8
+ two:
9
+ title: MyString
10
+ body: MyString
11
+ is_published: false
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class PostTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -1,4 +1,4 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe "navigation" do
3
+ RSpec.describe 'navigation' do
4
4
  end