natural_resource 0.1.2.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 471019cc52094c5009a4aca80d8a7410f92194f9
4
+ data.tar.gz: dffd007f8763759da6b7449f7f527f49b32dac6f
5
+ SHA512:
6
+ metadata.gz: 14f3e5cf9c857ee1a5dd91e0720924533fa91ff92b97715d22419901555d6833e9953b8174f2f4dc45648113c53973e82ddb17a5fe26755f134688f18ee2f1f0
7
+ data.tar.gz: 2ec45e30ef95b9699defecf3b5014bcf04d8ccd3ccba095ef9aaeb41ea2ef6fa26332a54d285dace3aa14f43cd2be8108279c603933a2f3a457900036919ddae
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Natural Resource
2
+
3
+ <img src="http://www.terracoding.com/assets/images/natural-resource.png" alt="Logo" height="200px"/>
4
+
5
+ Natural Resource is a small but opinionated framework built ontop of some of our favourite gems. It is designed to speed up CRUD related activities whilst still maintaining sensible standards (e.g. Admin Panel, expandable principles for an API). It utilises [Pundit](https://github.com/elabs/pundit) for authorisation and [Ransack](https://github.com/activerecord-hackery/ransack) for searching and filtering.
6
+
7
+ Sponsored by [Terracoding](http://terracoding.com)
8
+
9
+ # Getting Started
10
+
11
+ Simply install the gem
12
+
13
+ ``` ruby
14
+ gem 'natural_resource'
15
+ ```
16
+ Setup the Base Policy:
17
+ ``` sh
18
+ rails g natural:install
19
+ ```
20
+
21
+ Then generate controllers/policies as required:
22
+
23
+ ``` sh
24
+ rails g natural:resource controller_name optional_model_name
25
+ ```
26
+
27
+ The overall codebase for NaturalResource is very small and the bulk of the functionality can be found in `lib/natural_resource/controller.rb`, it's all relatively simple ruby code designed to be expanded on top for any custom functionality. To understand how Pundit works I suggest visiting their repository and going through the basics.
28
+
29
+ # Contributing
30
+
31
+ All contributions are welcome, simply fork the project and make a Pull Request upstream.
32
+
33
+ # Testing
34
+
35
+ Simply run the `rspec` test suite from the `spec/dummy` folder.
36
+
37
+ # License
38
+
39
+ Licensed under the MIT license, see the separate MIT-LICENSE.txt file.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
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 = 'NaturalResource'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Install the dependencies for using NaturalResource
3
+
4
+ Example:
5
+ rails generate natural:install
6
+
7
+ This will create:
8
+ app/policies/application_policy.rb
9
+
@@ -0,0 +1,9 @@
1
+ require 'rails/generators'
2
+
3
+ class Natural::InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def copy_application_policy
7
+ template 'application_policy.rb', 'app/policies/application_policy.rb'
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ class ApplicationPolicy < NaturalResource::Policy
2
+ class Scope < Scope
3
+ # It is recommended to use per-role scopes for Users # simply extend the
4
+ # resolve method with conditions for each "role" and the name of the scope
5
+ # they should call.
6
+ def resolve
7
+ if !user.is_a? User
8
+ anon_user_scope
9
+ else
10
+ standard_user_scope
11
+ end
12
+ end
13
+
14
+ def standard_user_scope
15
+ scope
16
+ # scope.where(user: user) # e.g. only show resources that belong to that user
17
+ end
18
+
19
+ def anon_user_scope
20
+ scope
21
+ # scope.public # e.g. only display public data
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ Description:
2
+ Generator to generate a natural_resource controller and it's dependencies.
3
+ Can be called via controller_name and with or without the model (inferred without).
4
+
5
+ Example:
6
+ rails generate natural:resource controller_name policy_model_name
7
+
8
+ This will create:
9
+ app/controllers/#{controller_name}.rb
10
+ app/policies/#{model_name}_policy.rb
11
+
12
+ rails generate natural:resource controller_name
13
+
14
+ This will create:
15
+ app/controllers/#{controller_name}.rb
16
+ app/policies/#{controller_name.singularize}_policy.rb
17
+
@@ -0,0 +1,29 @@
1
+ require 'pundit'
2
+ require 'rails/generators'
3
+
4
+ class Natural::ResourceGenerator < Rails::Generators::NamedBase
5
+ source_root File.expand_path('../templates', __FILE__)
6
+ argument :model_name, type: :string, default: ''
7
+
8
+ def generate_dependencies
9
+ @model_name = name if @model_name.blank?
10
+ invoke 'pundit:policy', [@model_name.singularize]
11
+ invoke 'controller', [name.pluralize]
12
+
13
+ c_sentinel = '< ApplicationController'
14
+
15
+ # Add resource reference to generated controller
16
+ in_root do
17
+ gsub_file "app/controllers/#{name.pluralize}_controller.rb", /(#{Regexp.escape(c_sentinel)})/mi do |match|
18
+ "#{match}\n resource :#{@model_name.pluralize.downcase}\n\n def resource_params\n params.require(:#{@model_name.singularize}).permit()\n end\n"
19
+ end
20
+ end
21
+
22
+ # Add note to generated Pundit policy
23
+ in_root do
24
+ gsub_file "app/policies/#{@model_name.singularize}_policy.rb", /(def resolve\n\s+scope\n\s+end)/mi do |match|
25
+ '# See ApplicationPolicy.rb to see how a scope is defined and used'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Natural
2
+
3
+ end
@@ -0,0 +1,11 @@
1
+ module Rspec
2
+ module Generators
3
+ class PolicyGenerator < ::Rails::Generators::NamedBase
4
+ source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
5
+
6
+ def create_policy_spec
7
+ template 'policy_spec.rb', File.join('spec/policies', class_path, "#{file_name}_policy_spec.rb")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ require '<%= File.exists?('spec/rails_helper.rb') ? 'rails_helper' : 'spec_helper' %>'
2
+
3
+ describe <%= class_name %>Policy do
4
+ let(:model) { } # <%= class_name %>.new
5
+ subject { described_class.new(user, model) }
6
+
7
+ context 'when a standard user' do
8
+ let(:user) { User.new }
9
+
10
+ # it { is_expected.to permit_auth(:action) }
11
+ end
12
+
13
+ context 'when an anon user' do
14
+ let(:user) { User.new }
15
+
16
+ # it { is_expected.to_not permit_auth(:action) }
17
+ end
18
+
19
+ describe <%= class_name %>Policy::Scope do
20
+ context 'when a standard user' do
21
+ let(:user) { } # User.new
22
+ end
23
+
24
+ context 'when a anon user' do
25
+ let(:user) { nil }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,93 @@
1
+ require 'active_support/concern'
2
+ require 'action_controller'
3
+ require 'pundit'
4
+ require 'ransack'
5
+ require 'will_paginate'
6
+
7
+ module NaturalResource
8
+ module Controller
9
+ extend ActiveSupport::Concern
10
+ include Pundit
11
+
12
+ class_methods do
13
+ def resource(resource_name)
14
+ after_action :verify_authorized, except: :index
15
+ after_action :verify_policy_scoped
16
+
17
+ helper_method :current_context, :query, :resource_class, :resource, :resources
18
+
19
+ rescue_from ActiveRecord::RecordInvalid, with: :handle_invalid_create_or_update
20
+
21
+ # GET /:controller_name/new
22
+ define_method :new do; end
23
+
24
+ # GET /:controller_name/edit
25
+ define_method :edit do; end
26
+
27
+ # GET /:controller_name
28
+ define_method :index do; end
29
+
30
+ # POST /:controller_name
31
+ define_method :create do
32
+ resource.update! resource_params
33
+ redirect_to success_path, {notice: "#{resource} was created."}
34
+ end
35
+
36
+ # PUT /:controller_name/:id
37
+ define_method :update do
38
+ resource.tap { |r| r.update! resource_params }.touch
39
+ redirect_to success_path, {notice: "#{resource} was updated."}
40
+ end
41
+
42
+ # DELETE /:controller_name/:id
43
+ define_method :destroy do
44
+ resource.destroy
45
+ redirect_to success_path, {notice: "#{resource} was deleted."}
46
+ end
47
+
48
+ private
49
+
50
+ define_method :current_context do; end
51
+
52
+ # Resource allocation methods
53
+ define_method :query do
54
+ @query ||= policy_scope(resource_scope).ransack(params[:q])
55
+ end
56
+
57
+ define_method :resource_class do
58
+ resource_name.to_s.singularize.camelcase.constantize
59
+ end
60
+
61
+ define_method :resource do
62
+ @resource ||= if ['new', 'create'].include?(action_name)
63
+ policy_scope(resource_scope).new
64
+ else
65
+ policy_scope(resource_scope).find(params[:id])
66
+ end.tap(&method(:authorize))
67
+ end
68
+
69
+ define_method :resources do
70
+ @resources ||= query.result.paginate(page: params[:page])
71
+ end
72
+
73
+ define_method :resource_scope do
74
+ resource_class
75
+ end
76
+
77
+ define_method :success_path do
78
+ url_for(action: :index)
79
+ end
80
+ end
81
+ end
82
+
83
+ def handle_invalid_create_or_update
84
+ case action_name
85
+ when 'create' then render :new
86
+ when 'update' then render :edit
87
+ else raise NotImplementedError
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ ActionController::Base.send :include, NaturalResource::Controller
@@ -0,0 +1,5 @@
1
+ String.class_eval do
2
+ def to_squawk
3
+ "squawk! #{self}".strip
4
+ end
5
+ end
@@ -0,0 +1,95 @@
1
+ require 'set'
2
+ require 'natural_resource/policy_preconditions'
3
+
4
+ # Starting block for ApplicationPolicy, set up for easy overriding
5
+ module NaturalResource
6
+ class Policy
7
+ include NaturalResource::PolicyPreconditions
8
+ attr_reader :user, :record
9
+
10
+ def initialize(user, record)
11
+ @user = user
12
+ @record = record
13
+ @errors = Hash.new { |h,k| h[k]= Set.new([]) }
14
+ @failure_status = nil
15
+ end
16
+
17
+ def failure_status
18
+ @failure_status.presence || :unauthorized
19
+ end
20
+
21
+ def errors
22
+ if @errors.keys.empty?
23
+ {user: ['does not have permission to perform this action']}
24
+ else
25
+ @errors
26
+ end
27
+ end
28
+
29
+ def index?
30
+ false
31
+ end
32
+
33
+ def show?
34
+ scope.where(id: record.id).exists?
35
+ end
36
+
37
+ def create?
38
+ false
39
+ end
40
+
41
+ def new?
42
+ create?
43
+ end
44
+
45
+ def update?
46
+ false
47
+ end
48
+
49
+ def edit?
50
+ update?
51
+ end
52
+
53
+ def destroy?
54
+ false
55
+ end
56
+
57
+ def own_record?
58
+ user && record.user == user
59
+ end
60
+
61
+ def anon?
62
+ user.nil?
63
+ end
64
+
65
+ def scope
66
+ Pundit.policy_scope!(user, record.class)
67
+ end
68
+
69
+ # Base Scope class to infer/scope what a user should see
70
+ class Scope
71
+ attr_reader :user, :scope
72
+
73
+ def initialize(user, scope)
74
+ @user = user
75
+ @scope = scope
76
+ end
77
+
78
+ def resolve
79
+ if !user.is_a?(User)
80
+ anon_user_scope
81
+ else
82
+ standard_user_scope
83
+ end
84
+ end
85
+
86
+ def standard_user_scope
87
+ raise NotImplementedError, [self.class.name, __method__].join('#')
88
+ end
89
+
90
+ def anon_user_scope
91
+ raise NotImplementedError, [self.class.name, __method__].join('#')
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,22 @@
1
+ # Encapsulation/Separation of precondition logic for Pundit policies
2
+ module NaturalResource::PolicyPreconditions
3
+ private
4
+
5
+ def precondition(record, precondition=nil, identifier: nil, message: nil)
6
+ precondition_met = if block_given?
7
+ yield
8
+ else
9
+ record.public_send(precondition)
10
+ end
11
+
12
+ identifier ||= record.class.to_s.underscore
13
+ message ||= "is not #{precondition.to_s.gsub(/\?*$/, '')}"
14
+
15
+ unless precondition_met
16
+ @failure_status ||= :precondition_failed
17
+ @errors[identifier] << message
18
+ end
19
+
20
+ precondition_met
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ module NaturalResource
2
+ class Railtie < Rails::Railtie
3
+ config.app_generators do |g|
4
+ g.templates.unshit File::expand_path('../../templates', __FILE__)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module NaturalResource
2
+ VERSION = "0.1.2.pre"
3
+ end
@@ -0,0 +1,9 @@
1
+ require 'natural_resource/version'
2
+ require 'natural_resource/controller'
3
+ require 'natural_resource/policy'
4
+ require 'generators/natural'
5
+ require 'generators/natural/resource/resource_generator'
6
+ require 'generators/natural/install/install_generator'
7
+
8
+ module NaturalResource
9
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :natural_resource do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: natural_resource
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2.pre
5
+ platform: ruby
6
+ authors:
7
+ - Robert White
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-08 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: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pundit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ransack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: will_paginate
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: haml-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
111
+ description:
112
+ email:
113
+ - robert@terracoding.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - README.md
119
+ - Rakefile
120
+ - lib/generators/natural.rb
121
+ - lib/generators/natural/install/USAGE
122
+ - lib/generators/natural/install/install_generator.rb
123
+ - lib/generators/natural/install/templates/application_policy.rb
124
+ - lib/generators/natural/resource/USAGE
125
+ - lib/generators/natural/resource/resource_generator.rb
126
+ - lib/generators/rspec/policy_generator.rb
127
+ - lib/generators/rspec/templates/policy_spec.rb
128
+ - lib/natural_resource.rb
129
+ - lib/natural_resource/controller.rb
130
+ - lib/natural_resource/core_ext.rb
131
+ - lib/natural_resource/policy.rb
132
+ - lib/natural_resource/policy_preconditions.rb
133
+ - lib/natural_resource/railtie.rb
134
+ - lib/natural_resource/version.rb
135
+ - lib/tasks/natural_resource_tasks.rake
136
+ homepage: http://www.github.com/haar/natural_resource
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">"
152
+ - !ruby/object:Gem::Version
153
+ version: 1.3.1
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.4.5.1
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Small Rails Framework to speed up CRUD controllers
160
+ test_files: []