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 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,8 @@
1
+ module CanCamel
2
+ extend ActiveSupport::Autoload
3
+ %i(Base Node SubjectNode ActionNode GroupNode ConcernNode Linter Filters
4
+ Validators Cache FilterHelper)
5
+ .each { |x| autoload(x) }
6
+
7
+ extend CanCamel::Base
8
+ end
@@ -0,0 +1,8 @@
1
+ module CanCamel
2
+ class ActionNode < Node
3
+ has_many :groups, foreign_key: :parent_id, class_name: "CanCamel::GroupNode"
4
+ belongs_to :parent, foreign_key: :parent_id, class_name: "CanCamel::SubjectNode"
5
+
6
+ validates_presence_of :parent_id
7
+ end
8
+ end
@@ -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,5 @@
1
+ module CanCamel
2
+ class ConcernNode < Node
3
+ validates :parent_id, absence: true
4
+ end
5
+ 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,7 @@
1
+ module CanCamel
2
+ class SubjectNode < Node
3
+ has_many :actions, foreign_key: :parent_id, class_name: "CanCamel::ActionNode"
4
+
5
+ validates :parent_id, absence: true
6
+ end
7
+ 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,3 @@
1
+ module CanCamel
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ # include custom ability filters
2
+ # require 'can_camel/filters'
3
+ # CanCamel::Filters.include AbilityFilters
4
+ CanCamel::TABLE_NAME = "can_camel_nodes"
@@ -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
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :can_camel do
3
+ # # Task goes here
4
+ # 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: []