programmable_scaffold_rails 0.0.2

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