resourceable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d30c0346ed98321cf31c90264b86017d3865abdabb2585d89060c9114e23b8a2
4
+ data.tar.gz: 74664ea69e227abf59e8e35be6c4ffa1b9fc8c4eb10bd8f4f6d6adc036b26b52
5
+ SHA512:
6
+ metadata.gz: 693e6df52c761140523fb2362f705c91d2b4673e81c49b72c97a01fbe9eef611d2a758e09bfaabc762ff90136bcb215724c5ca4f44e9f8e15988755ba2e1af21
7
+ data.tar.gz: 1cf5c2e2b6dca62caf1b793d817feaf41128464f890e6536e9a8cdf42b1f3de160623936faf86308ad7fbe513afef34a7b6bff1d422222864746c541d1efd491
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Jason Hazel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,126 @@
1
+ # Resourceable
2
+ Replaces the need for scaffolded controllers while not restricting the developer.
3
+
4
+ ## Live Stream
5
+
6
+ I've been live streaming the creation of this gem over on [Twitch](http://twitch.tv/jasonhazel). Videos are also posted to a [YouTube Playlist](https://www.youtube.com/playlist?list=PLi4UprHLU91BUYH1B5QMaRJnpP7vQzgu6)
7
+ ## Dependencies
8
+
9
+ * [CanCanCan](https://github.com/CanCanCommunity/cancancan) - loading and authorizing resource
10
+ * [Ransack](https://github.com/activerecord-hackery/ransack) - searching and sorting
11
+ * [Kaminari](https://github.com/kaminari/kaminari) - pagination
12
+
13
+ ## Installation
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'resourceable'
18
+ ```
19
+
20
+
21
+ ## Usage
22
+
23
+ Run the installer. Resourceable will also run the installers for Kaminari, CanCan and Responders.
24
+
25
+ ```
26
+ rails g resourceable:install
27
+ ```
28
+
29
+ ### CRUD
30
+ Replaces the need for scaffolded controller code. You will still need too create the views (for now).
31
+
32
+ ```ruby
33
+ class UsersController < ApplicationController
34
+ crud permitted: [ # strong params (default to {})
35
+ :email, :password
36
+ ],
37
+ cancan: { # cancan options (default is cancan default)
38
+ class_name: User,
39
+ id_params: :user_id
40
+ },
41
+ q: :search # ransack url variable (defaults to :q)
42
+ end
43
+ ```
44
+
45
+ Strong params can be defined in an instance method named `resource_params`
46
+
47
+ ```ruby
48
+ class UsersController < ApplicationController
49
+ crud
50
+
51
+ private
52
+
53
+ def resource_params
54
+ params.require(:user).permit(:email, :password)
55
+ end
56
+ end
57
+
58
+ ```
59
+
60
+ And of the regular CRUD actions can be done as one normally would. If you need some custom handling of resource creation, just define it. Resourceable will still handle any actions you haven't defined.
61
+
62
+ ```ruby
63
+ class UsersController < ApplicationController
64
+ crud permitted: [:email, :password]
65
+
66
+ def create
67
+ if @user.valid?
68
+ @user.save
69
+ UserMailer.new_user(@user).deliver_now
70
+ else
71
+ render :new
72
+ end
73
+ end
74
+ end
75
+
76
+ ```
77
+
78
+ ### Pagination
79
+
80
+ Pagination can be configured through the `pagination` option. Everything else is just plain Kaminari.
81
+
82
+
83
+ ```ruby
84
+ class UsersController < ApplicationController
85
+ crud pagination: {
86
+ param: :user_page, # default is :page,
87
+ per: 10, # default is 20
88
+ }
89
+ end
90
+ ```
91
+
92
+ ### SimpleForm Inputs
93
+
94
+ #### has_many
95
+
96
+ Resourceable adds a new SimpleForm input of `has_many` to simplify `has_many` associations. Resourceable will use the provided partial to display the associated objects. Don't forget to update the `permitted` params in your controller for the nested attributes.
97
+
98
+ ```ruby
99
+ class Task < ApplicationRecord
100
+ has_many :details
101
+ accepts_nested_attributes_for :details
102
+ end
103
+
104
+ class Detail < ApplicationRecord
105
+ belongs_to :task
106
+ end
107
+
108
+ ```
109
+
110
+ ```slim
111
+ / app/views/tasks/_form.slim
112
+ = simple_form_for @task do |f|
113
+ = f.input :details, as: :has_many, partial: 'tasks/detail_fields'
114
+ ```
115
+
116
+ ```slim
117
+ / app/views/tasks/_detail_fields.slim
118
+ = form_object.input :body
119
+ ```
120
+
121
+
122
+ ## Contributing
123
+ Contribution directions go here.
124
+
125
+ ## License
126
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,33 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Resourceable'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+
33
+ task default: :test
@@ -0,0 +1,28 @@
1
+ module Resourceable
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ # TODO: add options for the various generators
5
+ # things like using bootstrap themes for simple_form or kaminari
6
+ def responders
7
+ generate 'responders:install'
8
+ end
9
+
10
+ def cancan
11
+ generate 'cancan:ability'
12
+ end
13
+
14
+ def kaminari
15
+ generate 'kaminari:config'
16
+ generate 'kaminari:views -e slim'
17
+ end
18
+
19
+ def simple_form
20
+ generate 'simple_form:install'
21
+
22
+ inject_into_file 'config/initializers/simple_form.rb', after: ' # config.custom_inputs_namespaces << "CustomInputs"' do
23
+ "\n\tconfig.custom_inputs_namespaces << 'Resourceable::Inputs'"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ module Resourceable
2
+ module Generators
3
+ class ResourceGenerator < Rails::Generators::NamedBase
4
+ # rails g resourceable:resource User email:string password:string
5
+
6
+ class_option :attributes, type: :hash, banner: 'hash of model attributes', aliases: ['-a']
7
+
8
+ def model
9
+ generate 'model', "#{name} #{attributes_cli_options}"
10
+ end
11
+
12
+ def controller
13
+ generate 'controller', name.titleize.pluralize
14
+
15
+ puts "app/controllers/#{name.downcase.pluralize}_coontroller.rb"
16
+ puts "#{name.titleize.pluralize}Controller"
17
+
18
+ inject_into_class controller_path, controller_name,
19
+ "\tcrud permitted: #{attribute_keys}\n"
20
+ end
21
+
22
+ private
23
+
24
+ def attributes_cli_options
25
+ options[:attributes].map{ |k,v| "#{k}:#{v}" }.join(' ')
26
+ end
27
+
28
+ def attribute_keys
29
+ options[:attributes].symbolize_keys.keys
30
+ end
31
+
32
+ def controller_name
33
+ "#{name.titleize.pluralize}Controller"
34
+ end
35
+
36
+ def controller_path
37
+ "app/controllers/#{name.downcase.pluralize}_controller.rb"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+
2
+ # third party
3
+ require 'slim'
4
+ require 'cancancan'
5
+ require 'ransack'
6
+ require 'kaminari'
7
+ require 'responders'
8
+ require 'simple_form'
9
+
10
+ # patches
11
+ require 'resourceable/patches/cancan'
12
+
13
+ # our files
14
+ require 'resourceable/controllers/crud'
15
+ require 'resourceable/models/form_builder'
16
+
17
+ # simple_form inputs
18
+ require 'resourceable/inputs/has_many'
19
+
20
+ module Resourceable
21
+
22
+ end
@@ -0,0 +1,115 @@
1
+ module Resourceable
2
+ module Controllers
3
+ module CRUD
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def crud(options = {})
8
+ cattr_accessor :strong_params
9
+ cattr_accessor :cancan_options
10
+ cattr_accessor :search_param
11
+ cattr_accessor :pagination_params
12
+
13
+ self.strong_params = options.fetch(:permitted, [])
14
+ self.cancan_options = options.fetch(:cancan, {})
15
+ self.search_param = options.fetch(:q, :q)
16
+ self.pagination_params = pagination_defaults.merge(options.fetch(:pagination, {}))
17
+
18
+
19
+ include Resourceable::Controllers::CRUD::InstanceMethods
20
+ end
21
+
22
+ private
23
+
24
+ def pagination_defaults
25
+ { param: :page, per: 20 }
26
+ end
27
+ end
28
+
29
+ module InstanceMethods
30
+ extend ActiveSupport::Concern
31
+
32
+ included do
33
+ respond_to :html, :json
34
+
35
+ load_and_authorize_resource self.cancan_options
36
+ end
37
+
38
+ with_options to: :cancan_resource, prefix: false do |controller|
39
+ controller.delegate :collection_instance, :collection_instance=,
40
+ :resource_instance, :resource_instance=,
41
+ :instance_name
42
+ end
43
+
44
+ def index
45
+ @search = collection_instance.search(search_params)
46
+ collection_instance! @search.result.page(page_params).per(pagination.per)
47
+ respond_with collection_instance
48
+ end
49
+
50
+ def show
51
+ end
52
+
53
+ def new
54
+ end
55
+
56
+ def create
57
+ resource_instance.save
58
+ respond_with resource_instance
59
+ end
60
+
61
+ def edit
62
+ end
63
+
64
+ def update
65
+ resource_instance.update_attributes(resource_params)
66
+ respond_with resource_instance
67
+ end
68
+
69
+ def destroy
70
+ resource_instance.destroy
71
+ respond_with resource_instance
72
+ end
73
+
74
+ private
75
+
76
+ def flash_interpolation_options
77
+ { resource_name: resource_instance.class.to_s }
78
+ end
79
+
80
+ def page_params
81
+ params.fetch(pagination.param, nil)
82
+ end
83
+
84
+ def search_params
85
+ params.fetch(self.class.search_param, {})
86
+ end
87
+
88
+ def resource_params
89
+ params.require(instance_name.to_sym).permit(self.class.strong_params)
90
+ end
91
+
92
+ def pagination
93
+ OpenStruct.new(self.class.pagination_params)
94
+ end
95
+
96
+ protected
97
+
98
+ def cancan_resource
99
+ @cancan ||= self.class.cancan_resource_class.new(self)
100
+ end
101
+
102
+ def collection_instance!(collection)
103
+ send(:collection_instance=, collection)
104
+ end
105
+
106
+ def resource_instance!(resource)
107
+ send(:resource_instance=, resource)
108
+ end
109
+
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ ActionController::Base.send :include, Resourceable::Controllers::CRUD
@@ -0,0 +1,34 @@
1
+ module Resourceable
2
+ # simple_form might not play nice with namespaced inputs, we'll find out.
3
+ module Inputs
4
+ class HasManyInput < SimpleForm::Inputs::Base
5
+ def input
6
+ unless partial
7
+ # TODO: Implement proper exceptions.
8
+ raise 'You must specify a partial for `has_many` inputs.'
9
+ end
10
+ output = ActiveSupport::SafeBuffer.new
11
+
12
+ output << @builder.simple_fields_for(attribute_name )do |ff|
13
+ ff.input(:id, as: :hidden) + build_inputs(ff)
14
+ end
15
+
16
+ output
17
+ end
18
+
19
+ private
20
+
21
+ def partial
22
+ options.fetch(:partial, nil)
23
+ end
24
+
25
+ def action_view
26
+ @action_view ||= ActionView::Base.new(ActionController::Base.view_paths, {})
27
+ end
28
+
29
+ def build_inputs(form_object)
30
+ action_view.render(partial: partial, locals: { form_object: form_object }).html_safe
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,42 @@
1
+ module Resourceable
2
+ module Models
3
+ module FormBuilder
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def skip_input(*attributes)
8
+ cattr_accessor :skipped_inputs
9
+ self.skipped_inputs = (attributes || []).map(&:to_sym)
10
+
11
+ include_once Resourceable::Models::FormBuilder::InstanceMethods
12
+ end
13
+
14
+ def hidden_inputs(*attributes)
15
+
16
+ end
17
+
18
+ private
19
+
20
+ def include_once(module_name)
21
+ unless self.included_modules.include?(module_name)
22
+ include module_name
23
+ end
24
+ end
25
+ end
26
+
27
+ module InstanceMethods
28
+ extend ActiveSupport::Concern
29
+
30
+ def skipped_inputs
31
+ self.class.skipped_inputs
32
+ end
33
+
34
+ def skipped_input?(attribute_name)
35
+ skipped_inputs.include?(attribute_name.to_sym)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ ActiveRecord::Base.send :include, Resourceable::Models::FormBuilder
@@ -0,0 +1,11 @@
1
+ module CanCan
2
+ class ControllerResource
3
+ # TODO: make this less stupid, there has to be a better way than just making the methods public
4
+
5
+ public :collection_instance
6
+ public :collection_instance=
7
+ public :resource_instance
8
+ public :resource_instance=
9
+ public :instance_name
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Resourceable
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :resourceable do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resourceable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jason Hazel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-06-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.1.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.1.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: cancancan
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: kaminari
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: ransack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.8.4
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.8.4
69
+ - !ruby/object:Gem::Dependency
70
+ name: responders
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.4.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.4.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: slim
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simple_form
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 3.5.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 3.5.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Better than scaffolds
126
+ email:
127
+ - jason@hazel.codes
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - MIT-LICENSE
133
+ - README.md
134
+ - Rakefile
135
+ - lib/generators/resourceable/install_generator.rb
136
+ - lib/generators/resourceable/resource_generator.rb
137
+ - lib/resourceable.rb
138
+ - lib/resourceable/controllers/crud.rb
139
+ - lib/resourceable/inputs/has_many.rb
140
+ - lib/resourceable/models/form_builder.rb
141
+ - lib/resourceable/patches/cancan.rb
142
+ - lib/resourceable/version.rb
143
+ - lib/tasks/resourceable_tasks.rake
144
+ homepage: https://github.com/jasonhazel/resourceable
145
+ licenses:
146
+ - MIT
147
+ metadata: {}
148
+ post_install_message:
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ requirements: []
163
+ rubyforge_project:
164
+ rubygems_version: 2.7.3
165
+ signing_key:
166
+ specification_version: 4
167
+ summary: Simplify CRUD
168
+ test_files: []