can_camel 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/Rakefile +17 -0
- data/lib/can_camel.rb +8 -0
- data/lib/can_camel/action_node.rb +8 -0
- data/lib/can_camel/base.rb +16 -0
- data/lib/can_camel/cache.rb +31 -0
- data/lib/can_camel/concern_node.rb +5 -0
- data/lib/can_camel/filter_helper.rb +15 -0
- data/lib/can_camel/filters.rb +21 -0
- data/lib/can_camel/group_node.rb +17 -0
- data/lib/can_camel/linter.rb +52 -0
- data/lib/can_camel/node.rb +53 -0
- data/lib/can_camel/subject_node.rb +7 -0
- data/lib/can_camel/validators.rb +60 -0
- data/lib/can_camel/version.rb +3 -0
- data/lib/generators/can_camel/config/initializers/can_camel.rb +4 -0
- data/lib/generators/can_camel/db/migrate/00000000000001_create_can_camel_nodes.rb +18 -0
- data/lib/generators/can_camel/install_generator.rb +8 -0
- data/lib/tasks/can_camel_tasks.rake +4 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 19f8ade41da75b5b9b3beb17bc729b62fae529e3
|
4
|
+
data.tar.gz: 44fd15e4109320bc9143dd4acb992bed642955b3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f02195d10717ac041a2e64df9b893d8f2d14a91051bdaa87a3b5e4cc668dda8f4e8dc932744330d982905e8a2b216fbebc7ec7d033b02549dd3287b88e6638c2
|
7
|
+
data.tar.gz: 590a5bd7ec97794d11638630d45561b496f3279428ab8dadb83078b12b65437d6ca859de64694fe0a3ce30c7b2c6a869116f6f5c0de87bdd1e607e4b76878504
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
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 = 'CanCamel'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
Bundler::GemHelper.install_tasks
|
data/lib/can_camel.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module CanCamel
|
2
|
+
module Base
|
3
|
+
# don't delegate all, because lint_node designed to be run on same instances of linter
|
4
|
+
delegate :lint, :lint!, to: :new_linter
|
5
|
+
|
6
|
+
def can?(group, action, subject, **args)
|
7
|
+
Cache[[group, action, subject]].try(:can?, args)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def new_linter
|
13
|
+
CanCamel::Linter.new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CanCamel
|
2
|
+
module Cache
|
3
|
+
class <<self
|
4
|
+
delegate :[], to: :cache
|
5
|
+
|
6
|
+
def reload!
|
7
|
+
@cache = build_cache
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def cache
|
13
|
+
@cache ||= build_cache
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_cache
|
17
|
+
SubjectNode.all.each_with_object({}) do |subject_node, object|
|
18
|
+
subject_name = subject_node.name
|
19
|
+
subject_node.actions.each do |action_node|
|
20
|
+
action_name = action_node.name
|
21
|
+
action_node.groups.each do |group_node|
|
22
|
+
group_name = group_node.name
|
23
|
+
group_node.inherit!
|
24
|
+
object[[group_name, action_name, subject_name]] = group_node
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Holds functions to parse params from strings
|
2
|
+
module CanCamel
|
3
|
+
module FilterHelper
|
4
|
+
def from_int_array(str)
|
5
|
+
from_array(str).map(&:to_i)
|
6
|
+
rescue
|
7
|
+
raise Filters::ArgSyntaxError
|
8
|
+
end
|
9
|
+
|
10
|
+
def from_array(str)
|
11
|
+
raise Filters::ArgSyntaxError unless /\A\[(?<els>.+)\]\z/ =~ str
|
12
|
+
els.split ','
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'can_camel/validators'
|
2
|
+
module CanCamel::Filters
|
3
|
+
include CanCamel::Validators
|
4
|
+
extend CanCamel::FilterHelper
|
5
|
+
ArgSyntaxError = Class.new(StandardError)
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
validates :at_time, :hours, presence: {}
|
10
|
+
def at_time(hours:, **_)
|
11
|
+
hours = from_int_array hours
|
12
|
+
hours.include?(Time.now.hour) && {}
|
13
|
+
end
|
14
|
+
|
15
|
+
validates :at_day, presence_of: { fields: [:days, :wdays], any: true }
|
16
|
+
def at_day(days: nil, wdays: nil, **_)
|
17
|
+
return if days && !from_int_array(days).include?(Time.now.day)
|
18
|
+
return if wdays && !from_int_array(wdays).include?(Time.now.wday)
|
19
|
+
{}
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CanCamel
|
2
|
+
class GroupNode < Node
|
3
|
+
belongs_to :parent, foreign_key: :parent_id, class_name: "CanCamel::ActionNode"
|
4
|
+
|
5
|
+
validates_presence_of :parent_id
|
6
|
+
|
7
|
+
def can?(**additional_params)
|
8
|
+
condition.each_with_object(result.dup) do |hash, result|
|
9
|
+
hash.symbolize_keys!
|
10
|
+
raise Linter::LintingError unless hash[:method]
|
11
|
+
filtered = Filters.send(hash[:method], **hash.merge(additional_params))
|
12
|
+
return unless filtered
|
13
|
+
result.merge! filtered
|
14
|
+
end || result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module CanCamel
|
2
|
+
class Linter
|
3
|
+
LintingError = Class.new(StandardError)
|
4
|
+
|
5
|
+
def lint
|
6
|
+
%i(lint_inheritance lint_nodes).reduce(true) { |base, x| base && send(x) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def lint!
|
10
|
+
raise LintingError unless lint
|
11
|
+
end
|
12
|
+
|
13
|
+
def lint_node(node)
|
14
|
+
node = node.dup
|
15
|
+
node.inherit!
|
16
|
+
%i(validate_node).reduce(true) { |base, x| base && send(x, node) }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_node(node)
|
22
|
+
node.condition.each do |hash|
|
23
|
+
hash.symbolize_keys!
|
24
|
+
raise LintingError unless hash[:method]
|
25
|
+
Validators.validate!(filter: hash[:method], args: hash)
|
26
|
+
end
|
27
|
+
true
|
28
|
+
rescue Validators::ValidationError, LintingError
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def lint_nodes
|
33
|
+
CanCamel::Node.all.reduce(true) { |base, x| base && lint_node(x) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def lint_inheritance
|
37
|
+
CanCamel::Node.with_parents.where(<<-SQL).empty?
|
38
|
+
(
|
39
|
+
#{TABLE_NAME}.type = 'CanCamel::ActionNode'
|
40
|
+
AND
|
41
|
+
parents.type != 'CanCamel::SubjectNode'
|
42
|
+
)
|
43
|
+
OR
|
44
|
+
(
|
45
|
+
#{TABLE_NAME}.type = 'CanCamel::GroupNode'
|
46
|
+
AND
|
47
|
+
parents.type != 'CanCamel::ActionNode'
|
48
|
+
)
|
49
|
+
SQL
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module CanCamel
|
2
|
+
class Node < ActiveRecord::Base
|
3
|
+
self.table_name = TABLE_NAME
|
4
|
+
self.inheritance_column = 'type'
|
5
|
+
belongs_to :inherited_node, foreign_key: :inherit_id, class_name: "CanCamel::Node"
|
6
|
+
|
7
|
+
def self.with_parents
|
8
|
+
joins("LEFT JOIN #{TABLE_NAME} AS parents ON #{TABLE_NAME}.parent_id = parents.id")
|
9
|
+
end
|
10
|
+
|
11
|
+
def inherit!(source = nil)
|
12
|
+
return if inherited
|
13
|
+
@inherited = true
|
14
|
+
return inherit_source! source if source
|
15
|
+
|
16
|
+
inherit_source! parent if respond_to? :parent
|
17
|
+
inherit_source! inherited_node
|
18
|
+
end
|
19
|
+
|
20
|
+
validates! :inherited, absence: { message: "You should not save inherited nodes!" }
|
21
|
+
|
22
|
+
# accessors
|
23
|
+
|
24
|
+
def name
|
25
|
+
super.to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :inherited
|
31
|
+
|
32
|
+
def inherit_source!(source)
|
33
|
+
return unless source
|
34
|
+
|
35
|
+
source.inherit!
|
36
|
+
|
37
|
+
inherit_field(source, :description) { |x| self.description ||= x }
|
38
|
+
inherit_field(source, :result) { |x| result.merge!(x) { |_k, l, _r| l } }
|
39
|
+
|
40
|
+
inherit_field(source, :condition) do |new|
|
41
|
+
methods = condition.map { |c| c['method'] }
|
42
|
+
new.each do |x|
|
43
|
+
condition.push(x) unless methods.include? x['method']
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def inherit_field(source, field)
|
49
|
+
return if override_fields.include?(field.to_s)
|
50
|
+
yield source.send(field)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module CanCamel::Validators
|
2
|
+
ValidationError = Class.new(StandardError)
|
3
|
+
UnknownFilter = Class.new(ValidationError)
|
4
|
+
|
5
|
+
class <<self
|
6
|
+
def included(base)
|
7
|
+
base.define_singleton_method(:validates) { |*args| CanCamel::Validators.validates *args }
|
8
|
+
end
|
9
|
+
|
10
|
+
def validates(filter, field = nil, **args)
|
11
|
+
validators[filter] ||= []
|
12
|
+
args.each do |key, value|
|
13
|
+
validator =
|
14
|
+
if field
|
15
|
+
-> (x) { send(key, field: x[field], **value) }
|
16
|
+
else
|
17
|
+
-> (x) { send(key, args: x, **value) }
|
18
|
+
end
|
19
|
+
validators[filter].push(validator)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate!(filter:, args:)
|
24
|
+
filter = filter.to_sym
|
25
|
+
unless validators[filter]
|
26
|
+
raise UnknownFilter unless CanCamel::Filters.respond_to? filter
|
27
|
+
return
|
28
|
+
end
|
29
|
+
validators[filter].each { |x| x.call(args) }
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def validators
|
35
|
+
@validators ||= {}
|
36
|
+
end
|
37
|
+
|
38
|
+
def presence(field:)
|
39
|
+
raise ValidationError unless field.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
def presence_of(fields:, args:, any: false)
|
43
|
+
condition = fields.map { |x| args[x].present? }.reduce { |base, x| any ? (base || x) : (base && x) }
|
44
|
+
return if condition
|
45
|
+
raise ValidationError
|
46
|
+
end
|
47
|
+
|
48
|
+
def in(field:, array: [], empty: false)
|
49
|
+
return if array.include? field
|
50
|
+
return if empty && !field.present?
|
51
|
+
raise ValidationError
|
52
|
+
end
|
53
|
+
|
54
|
+
def custom(field:, lambda:, empty: false)
|
55
|
+
return if empty && !field.present?
|
56
|
+
return if lambda.call(field)
|
57
|
+
raise ValidationError
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateCanCamelNodes < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
reversible do |d|
|
4
|
+
d.up { execute 'CREATE extension IF NOT EXISTS hstore;' }
|
5
|
+
end
|
6
|
+
|
7
|
+
create_table CanCamel::TABLE_NAME do |t|
|
8
|
+
t.column :name, :string, index: true
|
9
|
+
t.column :description, :text, null: true
|
10
|
+
t.column :parent_id, :integer, null: true
|
11
|
+
t.column :inherit_id, :integer, null: true
|
12
|
+
t.column :type, :string
|
13
|
+
t.column :override_fields, :string, array: true, default: []
|
14
|
+
t.column :condition, :hstore, array: true, default: []
|
15
|
+
t.column :result, :hstore, default: {}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class CanCamel::InstallGenerator < Rails::Generators::Base
|
2
|
+
desc "Generates initializer and some migrations"
|
3
|
+
source_root(File.expand_path("..", __FILE__))
|
4
|
+
def install
|
5
|
+
copy_file 'config/initializers/can_camel.rb'
|
6
|
+
copy_file 'db/migrate/00000000000001_create_can_camel_nodes.rb'
|
7
|
+
end
|
8
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: can_camel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Smirnov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-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.2.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.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: |2
|
42
|
+
CanCamel allows to manage access levels with all the power of cancan style (saving semantics). It means, that you can use `can?` method anywhere you want to check privileges of a selected user to access something in some way. See readme file at https://github.com/JelF/can_camel/blob/master/README.md to learn more
|
43
|
+
email:
|
44
|
+
- begdory4@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- LICENSE
|
50
|
+
- Rakefile
|
51
|
+
- lib/can_camel.rb
|
52
|
+
- lib/can_camel/action_node.rb
|
53
|
+
- lib/can_camel/base.rb
|
54
|
+
- lib/can_camel/cache.rb
|
55
|
+
- lib/can_camel/concern_node.rb
|
56
|
+
- lib/can_camel/filter_helper.rb
|
57
|
+
- lib/can_camel/filters.rb
|
58
|
+
- lib/can_camel/group_node.rb
|
59
|
+
- lib/can_camel/linter.rb
|
60
|
+
- lib/can_camel/node.rb
|
61
|
+
- lib/can_camel/subject_node.rb
|
62
|
+
- lib/can_camel/validators.rb
|
63
|
+
- lib/can_camel/version.rb
|
64
|
+
- lib/generators/can_camel/config/initializers/can_camel.rb
|
65
|
+
- lib/generators/can_camel/db/migrate/00000000000001_create_can_camel_nodes.rb
|
66
|
+
- lib/generators/can_camel/install_generator.rb
|
67
|
+
- lib/tasks/can_camel_tasks.rake
|
68
|
+
homepage: https://github.com/JelF/can_camel
|
69
|
+
licenses:
|
70
|
+
- WTFPL
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.4.5
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: CanCamel is a cancan style access controll library
|
92
|
+
test_files: []
|