programmable_scaffold_rails 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +116 -0
  8. data/Rakefile +1 -0
  9. data/config.ru +9 -0
  10. data/config/action_controller.yml +12 -0
  11. data/config/locales/en.yml +8 -0
  12. data/lib/programmable_scaffold_rails.rb +17 -0
  13. data/lib/programmable_scaffold_rails/action_controller_extensions.rb +72 -0
  14. data/lib/programmable_scaffold_rails/action_controller_helpers.rb +163 -0
  15. data/lib/programmable_scaffold_rails/config.rb +15 -0
  16. data/lib/programmable_scaffold_rails/engine.rb +6 -0
  17. data/lib/programmable_scaffold_rails/scaffold/create.rb +51 -0
  18. data/lib/programmable_scaffold_rails/scaffold/destroy.rb +53 -0
  19. data/lib/programmable_scaffold_rails/scaffold/edit.rb +21 -0
  20. data/lib/programmable_scaffold_rails/scaffold/index.rb +20 -0
  21. data/lib/programmable_scaffold_rails/scaffold/new.rb +21 -0
  22. data/lib/programmable_scaffold_rails/scaffold/show.rb +21 -0
  23. data/lib/programmable_scaffold_rails/scaffold/update.rb +49 -0
  24. data/lib/programmable_scaffold_rails/version.rb +3 -0
  25. data/programmable_scaffold_rails.gemspec +34 -0
  26. data/spec/action_controller_extensions.rb_spec.rb +60 -0
  27. data/spec/action_controller_helpers_spec.rb +90 -0
  28. data/spec/controllers/dummies_controller_spec.rb +416 -0
  29. data/spec/factories.rb +10 -0
  30. data/spec/internal/app/controllers/dummies_controller.rb +9 -0
  31. data/spec/internal/app/models/dummy.rb +11 -0
  32. data/spec/internal/app/views/dummies/edit.html.erb +0 -0
  33. data/spec/internal/app/views/dummies/index.html.erb +0 -0
  34. data/spec/internal/app/views/dummies/index.json.builder +4 -0
  35. data/spec/internal/app/views/dummies/new.html.erb +0 -0
  36. data/spec/internal/app/views/dummies/show.html.erb +0 -0
  37. data/spec/internal/app/views/dummies/show.json.builder +1 -0
  38. data/spec/internal/config/database.yml +4 -0
  39. data/spec/internal/config/routes.rb +6 -0
  40. data/spec/internal/db/schema.rb +7 -0
  41. data/spec/internal/log/.gitignore +1 -0
  42. data/spec/internal/public/favicon.ico +0 -0
  43. data/spec/programmable_scaffold_rails_spec.rb +38 -0
  44. data/spec/spec_helper.rb +49 -0
  45. metadata +276 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1fd5eead3a11c4c4b4856264e9ce633d12aad3a6
4
+ data.tar.gz: 5bb03a34bfad4492c8a98c1e9463eec0fd127ddc
5
+ SHA512:
6
+ metadata.gz: 4770bb93ac76bd5b5ff6c2bded6b17630953bb13eb85732e6905bca7b800bf9d01e588f42e7fa6a78487b6940fcbb175a0cc5540fc6a8130460083f2fb2f20fa
7
+ data.tar.gz: 6c85ce9fc00f26d97b96334111d8efa29fba7d26e83729cb53fffb6b89d6b7fc0ab78f94dfcbc99e0ec2f52d8498a9ac6d26ac72d1b2f035924d80f91239be69
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p247
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in programmable_scaffold_rails.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'sqlite3', '~> 1.3.8'
8
+ gem 'activerecord', '~> 4.0.1'
9
+ gem 'actionpack', '~> 4.0.1'
10
+ gem 'rspec-rails', '~> 2.14', require: false
11
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Fire-Dragon-DoL
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # ProgrammableScaffoldRails
2
+
3
+ This gem should remove repeated code when rails scaffolding, expecially on controllers.
4
+ It's useful when you are prototyping and when you are just building an application with a lot of simple **CRUD**.
5
+ It's also configurable, to avoid as most as possible to completely override one of the default controllers method, unless you are doing something really special.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'programmable_scaffold_rails', '~> 0.0.2'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install programmable_scaffold_rails
20
+
21
+ ## Usage
22
+
23
+ In your Controller (or in ApplicationController if you want it application wise)
24
+ include the module:
25
+
26
+ ```ruby
27
+ include ProgrammableScaffoldRails::ActionControllerExtensions
28
+ ```
29
+
30
+ Now you have the class method `programmable_scaffold` available.
31
+ This method should be run once per controller where you want to use it
32
+ (options are inherited and can be overridden!). It will create each required
33
+ action to have a controller working correctly like if just generated with rails scaffolding.
34
+ All options have already default values, you can call the method without passing anything to it.
35
+
36
+ If you want have an idea on how it's working, I suggest you to check the
37
+ [Rails application](https://github.com/Fire-Dragon-DoL/programmable_scaffold_rails/tree/master/spec/internal)
38
+ used internally for specs.
39
+
40
+ ### Available options
41
+ | Name | Type | Default | Notes |
42
+ |------|------|---------|-------|
43
+ | class\_name | String | `controller.controller_name.to_s` | If set, replace the auto-calculated model class name |
44
+ | table\_name | String | `class_name.tableize` | This is used to name instance variables of a model, if set, it won't be auto-calculated |
45
+ | single\_instance\_name | String | `table_name.singularize` | Used when setting the instance variable for actions such as `new`, `edit`, `show`, `create`, `update` and `destroy` which can be used in the view |
46
+ | multiple\_instances\_name | String | `table_name.pluralize` | Used when setting the instance variable for action such as `index` which can be used in the view |
47
+ | cancan | bool | true | Enable cancan support which calls authorize! before each CRUD action |
48
+ | friendly\_id | bool | false | Enable `friendly_id` support, if your model uses `friendly_id`, that will be used for finding the model object |
49
+ | strong\_params | Symbol | :model_params | Name of existing method in controller, which should return params hash filtered by strong_parameters |
50
+ | url_\namespace | String | Empty string | When generating urls for `after_create_url`, `after_update_url` and `after_destroy_url`, a namespace will be prepended if this is set |
51
+ | after\_create\_action | Nil/Symbol | :show | One of `nil`, `:show`, `:edit`. `nil` and `:show` are the same |
52
+ | after\_update\_action | Nil/Symbol | :show | See `after_create_action` |
53
+ | after\_create\_url | Nil/Symbol/Proc/String | nil | If `nil` is set, the url will be auto generated with `url_namespace` and using the `after_create_action` for the given model object. If `Symbol` is used, a method with that name will be searched in controller and called with model instance as argument, it must generate a valid url. If proc is used, it will be called with model instance as argument. If a `String` is used, it will be directly passed to `url_for` to generate an url |
54
+ | after\_update\_url | Nil/Symbol/Proc/String | nil | See `after_create_url` |
55
+ | after\_destroy\_url | Nil/Symbol/Proc/String | nil | See `after_destroy_url` |
56
+ | formats | Array of Symbols | [:html, :json] | Formats returned by the CRUD actions for controller |
57
+
58
+ ### Example
59
+
60
+ ```ruby
61
+ # app/models/dummy.rb
62
+
63
+ class Dummy < ActiveRecord::Base
64
+ # column name: :string
65
+ end
66
+ ```
67
+
68
+ ```ruby
69
+ # app/controllers/dummies_controller.rb
70
+
71
+ class DummiesController < ApplicationController
72
+ include ProgrammableScaffoldRails::ActionControllerExtensions
73
+
74
+ programmable_scaffold class_name: 'Dummy',
75
+ table_name: 'dummies'
76
+
77
+ def model_params
78
+ params.require(:dummy).permit(:name)
79
+ end
80
+ end
81
+ ```
82
+
83
+ ### I18n
84
+
85
+ You can change these keys in your locales to have your application easily localized:
86
+
87
+ | Key | Value |
88
+ |-----|-------|
89
+ | `programmable_scaffold_rails.after_create_notice` | %{model_class} was successfully created. |
90
+ | `programmable_scaffold_rails.after_update_notice` | %{model_class} was successfully updated. |
91
+ | `programmable_scaffold_rails.after_destroy_notice` | %{model_class} was successfully destroyed. |
92
+ | `programmable_scaffold_rails.after_create_alert` | %{model_class} cannot be created. |
93
+ | `programmable_scaffold_rails.after_update_alert` | %{model_class} cannot be updated. |
94
+ | `programmable_scaffold_rails.after_destroy_alert` | %{model_class} cannot be destroyed. |
95
+
96
+ #### Regarding model_class
97
+
98
+ Model class is auto-calculated with `model_instance.class.model_name.human`, if you localize your model
99
+ class following rails standards, you shouldn't have any problem.
100
+
101
+ ## Features
102
+
103
+ - `I18n` messages for successfully or failed action
104
+ - [friendly_id](https://github.com/norman/friendly_id) support
105
+
106
+ # TODO
107
+
108
+ - Add I18n keys to completely replace those with `model_class`, if for example someone want to insert custom messages for each controller, something like `programmable_scaffold_rails.<controller_name>.after_create_notice`
109
+
110
+ ## Contributing
111
+
112
+ 1. Fork it
113
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
114
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
115
+ 4. Push to the branch (`git push origin my-new-feature`)
116
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/config.ru ADDED
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize! :active_record,
7
+ :action_controller,
8
+ :action_view
9
+ run Combustion::Application
@@ -0,0 +1,12 @@
1
+ cancan: true
2
+ friendly_id: false
3
+ strong_params: :model_params
4
+ # nil is used for :show (both can be used)
5
+ after_create_action: :show
6
+ after_update_action: :show
7
+ after_update_url: ~
8
+ after_create_url: ~
9
+ after_destroy_url: ~
10
+ formats:
11
+ - :html
12
+ - :json
@@ -0,0 +1,8 @@
1
+ en:
2
+ programmable_scaffold_rails:
3
+ after_create_notice: "%{model_class} was successfully created."
4
+ after_update_notice: "%{model_class} was successfully updated."
5
+ after_destroy_notice: "%{model_class} was successfully destroyed."
6
+ after_create_alert: "%{model_class} cannot be created."
7
+ after_update_alert: "%{model_class} cannot be updated."
8
+ after_destroy_alert: "%{model_class} cannot be destroyed."
@@ -0,0 +1,17 @@
1
+ require 'programmable_scaffold_rails/version'
2
+ require 'programmable_scaffold_rails/config'
3
+ require 'programmable_scaffold_rails/action_controller_extensions'
4
+
5
+ module ProgrammableScaffoldRails #:nodoc
6
+ end
7
+
8
+ # load Rails/Railtie
9
+ begin
10
+ require 'rails'
11
+ rescue LoadError
12
+ #do nothing
13
+ end
14
+
15
+ if defined? Rails
16
+ require 'programmable_scaffold_rails/engine'
17
+ end
@@ -0,0 +1,72 @@
1
+ require 'ostruct'
2
+ require 'active_support'
3
+ require 'active_support/core_ext'
4
+
5
+ require 'programmable_scaffold_rails/config'
6
+ require 'programmable_scaffold_rails/action_controller_helpers'
7
+ require 'programmable_scaffold_rails/scaffold/new'
8
+ require 'programmable_scaffold_rails/scaffold/create'
9
+ require 'programmable_scaffold_rails/scaffold/index'
10
+ require 'programmable_scaffold_rails/scaffold/show'
11
+ require 'programmable_scaffold_rails/scaffold/edit'
12
+ require 'programmable_scaffold_rails/scaffold/update'
13
+ require 'programmable_scaffold_rails/scaffold/destroy'
14
+
15
+ module ProgrammableScaffoldRails
16
+
17
+ module ActionControllerExtensions
18
+
19
+ extend ActiveSupport::Concern
20
+
21
+ module InstanceHelpers
22
+
23
+ def programmable_scaffold_controller_helpers
24
+ @programmable_scaffold_controller_helpers ||= ::ProgrammableScaffoldRails::ActionControllerHelpers.new(self)
25
+ end
26
+
27
+ end
28
+
29
+ module ClassMethods
30
+
31
+ # This is the core method which generate the scaffold things.
32
+ # It's important to notice that it uses a constant because it behaves like
33
+ # a class, can be inherited, overridden in child classes but it's "frozen"
34
+ # for the class where it's defined.
35
+ def programmable_scaffold(options={})
36
+ options = ::ProgrammableScaffoldRails::BASE_OPTIONS.merge(options)
37
+ crud = ::ProgrammableScaffoldRails::CRUD_METHODS
38
+
39
+ # Validate
40
+ if options.include?(:only) && options.include?(:except)
41
+ raise ":only and :except option must not be used toghether"
42
+ end
43
+
44
+ # Process
45
+ crud = options[:only] if options.include?(:only)
46
+ crud = crud - options[:except] if options.include?(:except)
47
+
48
+ send( :include, ProgrammableScaffoldRails::ActionControllerExtensions::InstanceHelpers )
49
+
50
+ send( :include, ProgrammableScaffoldRails::Scaffold::New ) if crud.include?(:new)
51
+ send( :include, ProgrammableScaffoldRails::Scaffold::Create ) if crud.include?(:create)
52
+ send( :include, ProgrammableScaffoldRails::Scaffold::Index ) if crud.include?(:index)
53
+ send( :include, ProgrammableScaffoldRails::Scaffold::Show ) if crud.include?(:show)
54
+ send( :include, ProgrammableScaffoldRails::Scaffold::Edit ) if crud.include?(:edit)
55
+ send( :include, ProgrammableScaffoldRails::Scaffold::Update ) if crud.include?(:update)
56
+ send( :include, ProgrammableScaffoldRails::Scaffold::Destroy ) if crud.include?(:destroy)
57
+
58
+ # Store
59
+ const_set(:PROGRAMMABLE_SCAFFOLD, OpenStruct.new(options).freeze)
60
+
61
+ self
62
+ end
63
+
64
+ def programmable_scaffold_options
65
+ const_get(:PROGRAMMABLE_SCAFFOLD)
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,163 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext'
3
+
4
+ module ProgrammableScaffoldRails
5
+
6
+ # Used to avoid polluting controller class with methods
7
+ class ActionControllerHelpers
8
+
9
+ def initialize(parent)
10
+ @parent = parent
11
+ end
12
+
13
+ def klass
14
+ return @klass if @klass
15
+
16
+ @klass = options[:class_name].try(:to_s) || controller.controller_name.to_s
17
+ @klass = @klass.classify.constantize
18
+ end
19
+
20
+ def table
21
+ return @table if @table
22
+
23
+ @table = options[:table_name].try(:to_s) || klass.to_s
24
+ @table = @table.tableize.to_sym
25
+ end
26
+
27
+ def cancan
28
+ return @cancan unless @cancan.nil?
29
+
30
+ @cancan = !!options[:cancan]
31
+ end
32
+
33
+ def single_instance_name
34
+ return @single_instance_name if @single_instance_name
35
+
36
+ @single_instance_name = options[:single_instance_name].try(:to_s) || table.to_s
37
+ @single_instance_name = @single_instance_name.singularize.to_sym
38
+ end
39
+
40
+ def single_instance
41
+ @single_instance ||= "@#{ single_instance_name }".to_sym
42
+ end
43
+
44
+ def multiple_instances_name
45
+ return @multiple_instances_name if @multiple_instances_name
46
+
47
+ @multiple_instances_name = options[:multiple_instances_name].try(:to_s) || table.to_s
48
+ @multiple_instances_name = @multiple_instances_name.pluralize.to_sym
49
+ end
50
+
51
+ def multiple_instances
52
+ @multiple_instances ||= "@#{ multiple_instances_name }".to_sym
53
+ end
54
+
55
+ def url_namespace
56
+ return @url_namespace if @url_namespace
57
+
58
+ @url_namespace = options[:url_namespace].try(:to_s) || ''
59
+ end
60
+
61
+ def after_create_action
62
+ @after_create_action = options[:after_create_action].try(:to_sym)
63
+ end
64
+
65
+ def after_update_action
66
+ @after_update_action = options[:after_update_action].try(:to_sym)
67
+ end
68
+
69
+ def formats
70
+ @formats ||= options[:formats]
71
+ end
72
+
73
+ def friendly_id
74
+ return @friendly_id unless @friendly_id.nil?
75
+
76
+ @friendly_id = !!options[:friendly_id]
77
+ end
78
+
79
+ def find_by_id_or_friendly_id(params)
80
+ found = nil
81
+ if friendly_id && klass.respond_to?(:friendly)
82
+ found = klass.friendly.find(params[:id])
83
+ else
84
+ found = klass.find(params[:id])
85
+ end
86
+
87
+ found
88
+ end
89
+
90
+ def strong_params
91
+ @strong_params ||= options[:strong_params].try(:to_sym)
92
+ end
93
+
94
+ def call_strong_params
95
+ unless controller.respond_to?(strong_params)
96
+ raise NotImplementedError, 'No strong_params method specified'
97
+ end
98
+ controller.send(strong_params)
99
+ end
100
+
101
+ def after_create_url(obj)
102
+ run_after_url_call_or_yield(:create, obj) { after_create_or_update_default_url(:create, obj) }
103
+ end
104
+
105
+ def after_update_url(obj)
106
+ run_after_url_call_or_yield(:update, obj) { after_create_or_update_default_url(:create, obj) }
107
+ end
108
+
109
+ def after_destroy_url(obj)
110
+ run_after_url_call_or_yield(:destroy, obj) do
111
+ if url_namespace.blank?
112
+ controller.url_for(multiple_instances_name)
113
+ else
114
+ controller.url_for([url_namespace, multiple_instances_name])
115
+ end
116
+ end
117
+ end
118
+
119
+ protected
120
+
121
+ def after_create_or_update_default_url(crud_action, obj)
122
+ after_action = send(:"after_#{ crud_action }_action")
123
+ if url_namespace.blank?
124
+ if after_action.nil? || after_action == :show
125
+ controller.url_for(obj)
126
+ else # :edit
127
+ controller.url_for([:edit, obj])
128
+ end
129
+ else
130
+ if after_action.nil? || after_action == :show
131
+ controller.url_for([url_namespace, obj])
132
+ else # :edit
133
+ controller.url_for([:edit, url_namespace, obj])
134
+ end
135
+ end
136
+ end
137
+
138
+ def controller
139
+ @parent
140
+ end
141
+
142
+ private
143
+
144
+ def options
145
+ @parent.class.programmable_scaffold_options
146
+ end
147
+
148
+ def run_after_url_call_or_yield(crud_action, obj)
149
+ after_url = options[:"after_#{ crud_action }_url"]
150
+ if after_url.class <= Symbol
151
+ controller.send(after_url, obj)
152
+ elsif after_url.class <= Proc
153
+ controller.instance_exec(obj, &after_url)
154
+ elsif after_url.class <= String
155
+ controller.url_for(after_url)
156
+ else
157
+ yield
158
+ end
159
+ end
160
+
161
+ end
162
+
163
+ end