Skip to content

Commit

Permalink
Add spec
Browse files Browse the repository at this point in the history
  • Loading branch information
dikond committed Mar 10, 2018
1 parent 207cc0c commit 1c86c8a
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 13 deletions.
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@
/pkg/
/spec/reports/
/tmp/

# rspec failure tracking
.rspec_status
/.ruby-version
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

# Specify your gem's dependencies in grabli.gemspec
gemspec
50 changes: 50 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
PATH
remote: .
specs:
grabli (0.1.0)
pundit (> 0)

GEM
remote: https://rubygems.org/
specs:
activesupport (5.1.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
concurrent-ruby (1.0.5)
diff-lcs (1.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
minitest (5.11.3)
pundit (1.1.0)
activesupport (>= 3.0.0)
rake (10.5.0)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.1)
thread_safe (0.3.6)
tzinfo (1.2.5)
thread_safe (~> 0.1)

PLATFORMS
ruby

DEPENDENCIES
bundler (~> 1.16)
grabli!
rake (~> 10.0)
rspec (~> 3.0)

BUNDLED WITH
1.16.0
13 changes: 13 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004

Copyright (C) 2018 dikond <[email protected]>

Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.

DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. You just DO WHAT THE FUCK YOU WANT TO.
3 changes: 2 additions & 1 deletion grabli.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Gem::Specification.new do |spec|
spec.version = Grabli::VERSION
spec.authors = ["dikond"]
spec.email = ["[email protected]"]
spec.license = "wtfpl"

spec.summary = "Grab permissions from your Pundit policies"
spec.homepage = "https://github.com/dikond/grabli"
Expand All @@ -15,7 +16,7 @@ Gem::Specification.new do |spec|

spec.require_paths = ["lib"]

spec.add_runtime_dependncy "pundit", "> 0"
spec.add_runtime_dependency "pundit", "> 0"
spec.add_development_dependency "bundler", "~> 1.16"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "rspec", "~> 3.0"
Expand Down
70 changes: 68 additions & 2 deletions lib/grabli.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,71 @@
require "grabli/version"
require "pundit"

module Grabli
# Your code goes here...
class Grabli
#
# Collect allowed policy permissions for the given user.
#
# @param user [Object] user object your policy work with
# @param subject [Symbol, Object] subject object your policy work with
# @return [Array<Symbol>] array of allowed policy permission
# @example
#
# Grabli.new.collect(@user, @company)
# #=> [:create?, :update?, :manage_occupied?]
#
# Grabli.new.collect(@user, :company)
# #=> [:create?]
#
def collect(user, subject)
policy_class(subject)
.public_instance_methods(false)
.reject! { |n| n =~ /permitted_attributes/ }
.each_with_object([]) do |permission, collection|
# allows to collect permissions without subject, for more @see Intruder
isubject = subject.is_a?(Symbol) ? Intruder.new(false) : subject
policy = policy_class(subject).new(user, isubject)

collection << permission if allowed? policy, permission
end
.sort
end

# Check whether certain permission is allowed.
#
# @param policy [ApplicationPolicy] instantiated policy
# @param permission [Symbol] permission name
# @return [Boolen, Object] true or false in case subject intruded
# or whatever you policy permission returns
# @example
#
# policy = Pundit.policy(@user, @company)
# Grabli.new.allowed?(policy, :create?)
# #=> true
#
def allowed?(policy, permission)
result = policy.public_send(permission)
return !policy.record.intruded if policy.record.is_a?(Intruder)
result
end

private def policy_class(record)
Pundit::PolicyFinder.new(record).policy
end

#
# When no subject specified by the user or the subject is a Symbol,
# we pass that object as a subject.
#
# If the subject isn't used it means we can add this permission as allowed.
# If it's used, CURRENTLY, we assume that the given permission isn't allowed.
#
# TODO: delegate to the original subject if it was given
# and intercept NoMethodError
#
Intruder = Struct.new(:intruded) do
def method_missing(*)
self[:intruded] = true
self
end
end
end
2 changes: 1 addition & 1 deletion lib/grabli/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Grabli
class Grabli
VERSION = "0.1.0"
end
38 changes: 36 additions & 2 deletions spec/grabli_spec.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,43 @@
require 'spec_helper'

RSpec.describe Grabli do
it "has a version number" do
expect(Grabli::VERSION).not_to be nil
end

it "does something useful" do
expect(false).to eq(true)
describe '#collect' do
subject(:permissions) { described_class.new.collect(current_user, subject) }

let(:current_user) { admin }
let(:subject) { user_invited_by_admin }
let(:admin) { User.new(true, nil) }
let(:user_invited_by_admin) { User.new(false, admin) }

it "doesn't include private methods" do
expect(permissions).not_to include(:manage_occupied?)
end

it "doesn't include permitted_attriubutes" do
expect(permissions).not_to include(:permitted_attriubutes)
expect(permissions).not_to include(:permitted_attributes_for_create)
end

context 'with domain object subject' do
let(:current_user) { admin }
let(:subject) { user_invited_by_admin }

it 'includes allowed permissions which depend on subject' do
expect(permissions).to match_array %i[create? update?]
end
end

context 'without domain object subject' do
let(:current_user) { admin }
let(:subject) { :user }

it "doesn't include permissions which depend on subject" do
expect(permissions).to match_array %i[create?]
end
end
end
end
30 changes: 27 additions & 3 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,37 @@
require "grabli"

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = ".rspec_status"

# Disable RSpec exposing methods globally on `Module` and `main`
config.disable_monkey_patching!

config.expect_with :rspec do |c|
c.syntax = :expect
end
end

class UserPolicy < Struct.new(:current_user, :record)
def create?
current_user.admin
end

def update?
manage_occupied? || current_user.admin
end

def permitted_attributes
%i[foo]
end

def permitted_attributes_for_create
%i[foo bar]
end

private

def manage_occupied?
record.invited_by == current_user
end
end

class User < Struct.new(:admin, :invited_by)
end

0 comments on commit 1c86c8a

Please sign in to comment.