controller_resources 0.0.5 → 0.0.6

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 (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