cancan-export 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ab8f4923b92deaad80e7e97bc00cc52b61c4cf23
4
+ data.tar.gz: b8348cc0d0f60d391644fcbec7a903dea69fd257
5
+ SHA512:
6
+ metadata.gz: a819e03e5f4abf8633706c04e655f06271695116a91efad3855a52d03303358c388e6ec0ce9b672b0d6d73e6c80bc94f0e48da8775cbf4e1631f2809027f8f85
7
+ data.tar.gz: 5196e4031a3cbd80c5a7e88e8617c4c0d6a3b2138be478049185d7f85764e6cc7b8c186e0c8da11327550f090c3ceb4c15782fc51e68a80df3e05c1f792cb770
@@ -0,0 +1,19 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /spec/examples.txt
10
+ /tmp/
11
+
12
+ .directory
13
+ *.log
14
+ *.bak
15
+ *.old
16
+ *.gem
17
+
18
+ /auth.yml
19
+ /feed.json
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cancan-export.gemspec
4
+ gemspec
@@ -0,0 +1,35 @@
1
+ # Cancan::Export
2
+
3
+ It is the rewrite of a vital parts of CanCan v.1.13.1 classes: Ability and Rule.
4
+ All method names writen in a snake case is rewrites of the CanCan ones.
5
+
6
+ This rewrite is simplified since we don't have ORM objects and real DB relations.
7
+ All we have on the client-side is plain old objects,
8
+ though as long as each object has `_type' or `type' property, it can be identified as an object of a specific model.
9
+
10
+ ## Usage
11
+
12
+ ```javascript
13
+ ability = new CanCanAbility(gon)
14
+ ability.can('read', 'CustomerOrder')
15
+ // => true or false
16
+ customerOrder = {partner_id: 123}
17
+ // or with restmod
18
+ customerOrder = CustomerOrder.$build({ partner_id: 123 })
19
+ ability.can('edit', customerOrder)
20
+ // => true, if a partner can edit only his orders and gon.user.partner_id == 123
21
+ ```
22
+
23
+ ### With angular
24
+
25
+ ```javascript
26
+ // include CanCan service into the $rootScope,
27
+ MyApp.run(['$rootScope', 'CanCan', function($rootScope, CanCan) {
28
+ angular.extend($rootScope, CanCan);
29
+ ...
30
+ }])
31
+ // then, in templates you can authorize parts like
32
+ can('edit', customerOrder)
33
+ // and check the permissions in a controller like
34
+ $scope.can('edit', $scope.customerOrder)
35
+ ```
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # Usage:
2
+ # include CanCan service into the $rootScope,
3
+ # MyApp.run('MyApp', ['$rootScope', 'CanCan', ($rootScope, CanCan) ->
4
+ # angular.extend $rootScope, CanCan
5
+ # ...
6
+ # ])
7
+ # then, in templates you can authorize parts like
8
+ # can('edit', customerOrder)
9
+ # and check the permissions in a controller like
10
+ # $scope.can('edit', $scope.customerOrder)
11
+ ((global, factory) ->
12
+ if typeof define == 'function' and define.amd?
13
+ define ['angular', 'cancan-export'], factory
14
+ else
15
+ factory(global.angular, global.CanCanAbility)
16
+ )(this, (angular, CanCanAbility) ->
17
+ angular
18
+ .module 'cancan.export', []
19
+ .service 'CanCan', ->
20
+ ability: new CanCanAbility(window.gon)
21
+ getAbility: (object) -> @ability = new CanCanAbility(object)
22
+ can: (action, subject, argsForBlock...) -> @ability.can action, subject, argsForBlock...
23
+ cannot: (action, subject, argsForBlock...) -> @ability.cannot action, subject, argsForBlock...
24
+ )
@@ -0,0 +1,148 @@
1
+ # It is the rewrite of a vital parts of CanCan v.1.13.1 classes: Ability and Rule.
2
+ # All method names writen in a snake case is rewrites of the CanCan ones.
3
+ #
4
+ # This rewrite is simplified since we don't have ORM objects and real DB relations.
5
+ # All we have on the client-side is plain old objects,
6
+ # though as long as each object has `_type' or `type' property, it can be identified as an object of a specific model.
7
+ #
8
+ # Usage:
9
+ # ability = new CanCanAbility(gon)
10
+ # ability.can('read', 'CustomerOrder')
11
+ # # => true or false
12
+ # customerOrder = {partner_id: 123}
13
+ # # or with restmod
14
+ # customerOrder = CustomerOrder.$build({ partner_id: 123 });
15
+ # ability.can('edit', customerOrder)
16
+ # # => true, if a partner can edit only his orders and gon.user.partner_id == 123
17
+ ((global, factory) ->
18
+ if typeof define == 'function' and define.amd?
19
+ define ['lodash'], factory
20
+ else
21
+ factory(global._)
22
+ )(this, (_) ->
23
+ class @CanCanAbility
24
+ rules: []
25
+ rulesIndex: {}
26
+ helpers: []
27
+
28
+ # @ data : [gon Object]
29
+ constructor: (data) ->
30
+ @user = data.user
31
+ $.extend @, data.ability
32
+
33
+ # Import each helper defined in ruby's Ability object.
34
+ # `this' must be the object in which context `user' property is defined, thus it is Ability itself
35
+ for name, source in @helpersSource
36
+ @[name] = $.proxy eval(source), @
37
+
38
+ @rules = @rules.map (data) =>
39
+ new Rule(data, @)
40
+
41
+ can: (action, subject, extra_args...) ->
42
+ relevant_rules = @relevant_rules(action, subject)
43
+
44
+ match = _(relevant_rules).find (rule) =>
45
+ rule.matches_conditions(action, subject, extra_args)
46
+
47
+ match and match.baseBehavior or false
48
+
49
+ cannot: (action, object, extra_args...) ->
50
+ !@can action, object, extra_args...
51
+
52
+
53
+ typeOf: (subject) ->
54
+ if typeof subject == 'string'
55
+ subject
56
+ else
57
+ subject._type or subject.type
58
+
59
+ matches_subject: (subjects, subject) ->
60
+ _(subjects).include('all') or # wildcard rule
61
+ _(subjects).include(@typeOf subject)
62
+
63
+ relevant_rules: (action, subject) ->
64
+ _.clone(@rules).reverse().filter (rule) =>
65
+ @matches_subject(rule.subjects, subject) and rule.is_relevant action, subject
66
+
67
+
68
+ class Rule
69
+ # there are nor subjects nor actions
70
+ matchAll: false
71
+ # can or cannot?
72
+ baseBehavior: true
73
+ # what to do?
74
+ actions: []
75
+ # object model names
76
+ # N.B. a Class starts with a Capital letter; a plain string/symbol with a small letter
77
+ subjects: []
78
+ # `where' hash on a model
79
+ conditions: {}
80
+ # function to check against
81
+ block: null
82
+
83
+ constructor: (data, ability) ->
84
+ $.extend @, data
85
+ @ability = ability
86
+ @block = $.proxy eval(data.blockSource), ability
87
+
88
+ typeOf: CanCanAbility.prototype.typeOf
89
+
90
+ is_relevant: (action, subject) ->
91
+ @matchAll or (@matches_action(action) and @matches_subject(subject))
92
+
93
+ matches_action: (action) ->
94
+ _(@actions).include('manage') or # wildcard rule
95
+ _(@actions).include(action)
96
+
97
+ matches_subject: (subject) ->
98
+ @ability.matches_subject(@subjects, subject)
99
+
100
+ # Nested subjects matching is not supported now
101
+ matches_conditions: (action, subject, extra_args) ->
102
+ if @matchAll
103
+ @call_block_with_all(action, subject, extra_args)
104
+ else if @block && typeof subject != 'string'
105
+ @block(subject, extra_args...)
106
+ else if @conditions && typeof subject != 'string'
107
+ @matches_conditions_hash(subject)
108
+ else
109
+ # Don't stop at "cannot" definitions when there are conditions.
110
+ # We want, if @baseBehavior is false, Ability#relevant_rules to not accept the result and check the next rule.
111
+ !@conditions or $.isEmptyObject(@conditions) or @baseBehavior
112
+
113
+ # This is not suported yet. Use of this hook implies that we're able to pass a _class_ into block function,
114
+ # but for the while we can't
115
+ call_block_with_all: (action, subject, extra_args) ->
116
+ if typeof subject == 'string'
117
+ @block(action, subject, nil, extra_args...)
118
+ else
119
+ @block(action, @typeOf(subject), subject, extra_args...)
120
+
121
+ # @ subject : [resource Object] : a pattern we match against
122
+ # @ conditions : [Object] : a pattern we match against
123
+ matches_conditions_hash: (subject, conditions=@conditions) ->
124
+ return true if $.isEmptyObject conditions
125
+ every_match = not _(conditions).find (value, name) =>
126
+ !@condition_match(subject[name], value)
127
+
128
+ # @ attribute : a value which we match
129
+ # @ value : a pattern we match against
130
+ condition_match: (attribute, value) ->
131
+ if $.isPlainObject value
132
+ @hash_condition_match(attribute, value)
133
+ else if $.isArray value
134
+ # match against any of the rule condition values will do
135
+ _(value).include(attribute)
136
+ else
137
+ # only exact equality will do
138
+ attribute == value
139
+
140
+ # @ attribute : a value which we match
141
+ # @ value : [Object] : a pattern we match against
142
+ hash_condition_match: (attribute, value) ->
143
+ if $.isArray attribute
144
+ # match of any element of [Array] attribute against the rule condition hash will do (recursion)
145
+ _(attribute).find (element) => @matches_conditions_hash(element, value)
146
+ else
147
+ attribute? && @matches_conditions_hash?(attribute, value)
148
+ )
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cancan/export/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cancan-export"
8
+ spec.version = CanCan::Export::VERSION
9
+ spec.authors = ["Sergey Baev"]
10
+
11
+ spec.summary = "Exports CanCan rules to the client-side."
12
+ spec.homepage = "https://github.com/tinbka/cancan-export"
13
+
14
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_development_dependency "bundler", "~> 1.10"
18
+ spec.add_development_dependency "rake", "~> 10.0"
19
+
20
+ spec.add_dependency "gon"
21
+ spec.add_dependency "coffee-rails"
22
+ end
@@ -0,0 +1,23 @@
1
+ require "cancan"
2
+ require "gon"
3
+
4
+ require "cancan/export/version"
5
+ require "cancan/export/compiler"
6
+ require "cancan/export/helpers"
7
+ require "cancan/export/engine"
8
+ require "cancan/export/ext/ability"
9
+ require "cancan/export/ext/rule"
10
+
11
+ module CanCan
12
+ class Rule
13
+ include Export::Rule
14
+ end
15
+
16
+ module Ability
17
+ include Export::Ability
18
+ end
19
+
20
+ module Export
21
+ ::ActionController::Base.send :include, ControllerHelpers
22
+ end
23
+ end
@@ -0,0 +1,57 @@
1
+ module CanCan
2
+ module Export
3
+ class Compiler
4
+
5
+ def initialize
6
+ @mtimes = {}
7
+ @lines = {}
8
+ @js = {}
9
+ end
10
+
11
+ def to_js(block)
12
+ source_lines = definition(block)
13
+ return @js[source_lines] if @js[source_lines]
14
+
15
+ case block
16
+ when Proc then source_lines[0] =~ / (?:do|{)\s*(?:\|([\s\w\d,]+)\|)?\s*$/
17
+ when Method then source_lines[0] =~ /def [\w\d\.]+\(?([\s\w\d,]+)\)?\s*$/
18
+ end
19
+ arguments = $1.scan(/\w+/)
20
+
21
+ coffeefied_source_lines = source_lines[1..-1]*"\n"
22
+ coffeefied_source_lines.gsub!(/\.(\w+)\?/, '.is_\1()') # .present? -> .present()
23
+ coffeefied_source_lines.gsub!(/\.(\w+)!/, '.do_\1()') # .sub! -> .sub!()
24
+ coffeefied_source_lines.gsub!(/(\d+)\.([a-z_]+)/, '(\1).\2()') # 4.hours -> (4).hours()
25
+ coffeefied_source_lines.gsub!(/:([\w\d]+)/, '"\1"') # 4.hours -> (4).hours()
26
+ #$log.info coffeefied_source_lines, caller_at: [0, 9..20]
27
+
28
+ @js[source_lines] = CoffeeScript.compile "(#{arguments*', '}) ->\n#{coffeefied_source_lines}", bare: true
29
+ end
30
+
31
+ private
32
+
33
+ def definition(proc)
34
+ file, line = proc.source_location
35
+ lines = readlines file
36
+
37
+ definition_line = lines[line-1]
38
+ indentation = definition_line[/^\s*/]
39
+ return lines[line-1..-1].take_while {|line| line !~ /#{indentation}end$/}
40
+ end
41
+
42
+ def readlines(file)
43
+ if File.exists?(file)
44
+ mtime = File.mtime(file)
45
+ unless @mtimes[file] and @mtimes[file] >= mtime
46
+ @mtimes[file] = mtime
47
+ @lines[file] = IO.readlines(file)
48
+ end
49
+ @lines[file]
50
+ else
51
+ raise Errno::ENOENT, "The file where a block was defined, does not exist anymore @ rb_sysopen - #{file}"
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,9 @@
1
+ module CanCan
2
+ module Export
3
+ class Engine < ::Rails::Engine
4
+ initializer 'cancan.export' do |app|
5
+ app.config.assets.paths << root.join('assets', 'javascripts').to_s
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ module CanCan
2
+ module Export
3
+ module Ability
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ @@compiler = CanCan::Export::Compiler.new
8
+ end
9
+
10
+ def helper_method(*method_names)
11
+ helper_methods.merge! method_names.map {|method_name|
12
+ [method_name, @@compiler.to_js(method(method_name))]
13
+ }.to_h
14
+ end
15
+
16
+ def helper_methods
17
+ @helpers ||= {}
18
+ end
19
+
20
+ def to_json(options={})
21
+ {
22
+ rules: @rules.map {|rule|
23
+ rule.compile(self, @@compiler)
24
+ },
25
+ rulesIndex: @rules_index.map {|object, indices|
26
+ [object.to_s, indices]
27
+ }.to_h,
28
+ helpersSource: helper_methods
29
+ }.to_json(options)
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ module CanCan
2
+ module Export
3
+ module Rule
4
+
5
+ def compile(ability, compiler)
6
+ {
7
+ matchAll: @match_all,
8
+ baseBehavior: @base_behavior,
9
+ subjects: @subjects.map(&:to_s),
10
+ actions: ability.send(:expand_actions, @actions).map(&:to_s),
11
+ conditions: @conditions,
12
+ blockSource: @block && compiler.to_js(@block)
13
+ }
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module CanCan
2
+ module Export
3
+ module ControllerHelpers
4
+
5
+ def gon
6
+ super.tap do |g|
7
+ if current_user
8
+ g.user ||= current_user
9
+ g.ability ||= ::Ability.new(g.user)
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module CanCan
2
+ module Export
3
+ VERSION = "0.2.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cancan-export
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Sergey Baev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gon
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: coffee-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - ".gitignore"
76
+ - ".travis.yml"
77
+ - Gemfile
78
+ - README.md
79
+ - Rakefile
80
+ - assets/javascripts/cancan/export-angular.js.coffee
81
+ - assets/javascripts/cancan/export.js.coffee
82
+ - cancan-export.gemspec
83
+ - lib/cancan/export.rb
84
+ - lib/cancan/export/compiler.rb
85
+ - lib/cancan/export/engine.rb
86
+ - lib/cancan/export/ext/ability.rb
87
+ - lib/cancan/export/ext/rule.rb
88
+ - lib/cancan/export/helpers.rb
89
+ - lib/cancan/export/version.rb
90
+ homepage: https://github.com/tinbka/cancan-export
91
+ licenses: []
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.8
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Exports CanCan rules to the client-side.
113
+ test_files: []