monarchy 0.2.1

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: f93490c3902cf4b5f6a3c392da407d0ff72d1f27
4
+ data.tar.gz: 367daf68a7dcb3ea36fc5956cb005e466cd46b4e
5
+ SHA512:
6
+ metadata.gz: 13671f970ef72842f9619f551e9ab20fd3c0a723f2da362389b01eea2635bbf8011f546326b5c160aa1d08601962d48390474f899ffb1b707cb2086108d5d365
7
+ data.tar.gz: 597b0cb30c74379abea9e8c6e4eb2895754ba0d4183279ef8315547a43e46e2f4e7bbf6ce4e0d14e8aedfafe732df566bbeeb7012e2461774658e682bf1af8b5
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ spec/dummy/log/*.log
5
+ spec/dummy/db/*.sqlite3
6
+ spec/dummy/db/*.sqlite3-journal
7
+ spec/dummy/log/*.log
8
+ spec/dummy/tmp/
9
+ spec/dummy/.sass-cache
10
+ .DS_Store
11
+ coverage/
12
+ .lock-*
File without changes
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,19 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Exclude:
4
+ - 'spec/dummy/db/seeds.rb'
5
+ - 'spec/dummy/db/migrate/**'
6
+ - 'spec/dummy/db/schema.rb'
7
+ - 'lib/generators/templates/**/*'
8
+ Documentation:
9
+ Enabled: false
10
+ Metrics/LineLength:
11
+ Max: 120
12
+ Style/CaseIndentation:
13
+ Enabled: false
14
+ Style/StructInheritance:
15
+ Enabled: false
16
+ Style/ClassAndModuleChildren:
17
+ Enabled: false
18
+ Rails/HasAndBelongsToMany:
19
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.10.6
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+ ruby '2.3.0'
4
+
5
+ # Declare your gem's dependencies in monarchy.gemspec.
6
+ # Bundler will treat runtime dependencies like base dependencies, and
7
+ # development dependencies will be added by default to the :development group.
8
+ gemspec
9
+
10
+ # Declare any dependencies that are still in development here instead of in
11
+ # your gemspec. These might include edge Rails or gems from your path or
12
+ # Git. Remember to move these dependencies to your gemspec before releasing
13
+ # your gem to rubygems.org.
14
+
15
+ # To use a debugger
16
+ # gem 'byebug', group: [:development, :test]
17
+
18
+ gem 'configurations', '~> 2.2.0'
19
+
20
+ group :development, :test do
21
+ gem 'sqlite3'
22
+ gem 'rubocop'
23
+ gem 'pry-rails', '0.3.4'
24
+ gem 'shoulda-matchers'
25
+ gem 'rspec-rails'
26
+ gem 'factory_girl_rails', '4.5.0'
27
+ gem 'ffaker', '2.1.0'
28
+ gem 'database_cleaner', '1.4.1'
29
+ end
30
+
31
+ gem 'simplecov', require: false, group: :test
data/Gemfile.lock ADDED
@@ -0,0 +1,190 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ monarchy (0.2.1)
5
+ closure_tree (= 6.0.0)
6
+ configurations (= 2.2.0)
7
+ rails (~> 4.2, >= 4.2.4)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionmailer (4.2.6)
13
+ actionpack (= 4.2.6)
14
+ actionview (= 4.2.6)
15
+ activejob (= 4.2.6)
16
+ mail (~> 2.5, >= 2.5.4)
17
+ rails-dom-testing (~> 1.0, >= 1.0.5)
18
+ actionpack (4.2.6)
19
+ actionview (= 4.2.6)
20
+ activesupport (= 4.2.6)
21
+ rack (~> 1.6)
22
+ rack-test (~> 0.6.2)
23
+ rails-dom-testing (~> 1.0, >= 1.0.5)
24
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
25
+ actionview (4.2.6)
26
+ activesupport (= 4.2.6)
27
+ builder (~> 3.1)
28
+ erubis (~> 2.7.0)
29
+ rails-dom-testing (~> 1.0, >= 1.0.5)
30
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
31
+ activejob (4.2.6)
32
+ activesupport (= 4.2.6)
33
+ globalid (>= 0.3.0)
34
+ activemodel (4.2.6)
35
+ activesupport (= 4.2.6)
36
+ builder (~> 3.1)
37
+ activerecord (4.2.6)
38
+ activemodel (= 4.2.6)
39
+ activesupport (= 4.2.6)
40
+ arel (~> 6.0)
41
+ activesupport (4.2.6)
42
+ i18n (~> 0.7)
43
+ json (~> 1.7, >= 1.7.7)
44
+ minitest (~> 5.1)
45
+ thread_safe (~> 0.3, >= 0.3.4)
46
+ tzinfo (~> 1.1)
47
+ arel (6.0.3)
48
+ ast (2.2.0)
49
+ builder (3.2.2)
50
+ closure_tree (6.0.0)
51
+ activerecord (>= 4.1.0)
52
+ with_advisory_lock (>= 3.0.0)
53
+ coderay (1.1.1)
54
+ concurrent-ruby (1.0.1)
55
+ configurations (2.2.0)
56
+ database_cleaner (1.4.1)
57
+ diff-lcs (1.2.5)
58
+ docile (1.1.5)
59
+ erubis (2.7.0)
60
+ factory_girl (4.5.0)
61
+ activesupport (>= 3.0.0)
62
+ factory_girl_rails (4.5.0)
63
+ factory_girl (~> 4.5.0)
64
+ railties (>= 3.0.0)
65
+ ffaker (2.1.0)
66
+ globalid (0.3.6)
67
+ activesupport (>= 4.1.0)
68
+ i18n (0.7.0)
69
+ json (1.8.3)
70
+ loofah (2.0.3)
71
+ nokogiri (>= 1.5.9)
72
+ mail (2.6.4)
73
+ mime-types (>= 1.16, < 4)
74
+ method_source (0.8.2)
75
+ mime-types (3.0)
76
+ mime-types-data (~> 3.2015)
77
+ mime-types-data (3.2016.0221)
78
+ mini_portile2 (2.0.0)
79
+ minitest (5.8.4)
80
+ nokogiri (1.6.7.2)
81
+ mini_portile2 (~> 2.0.0.rc2)
82
+ parser (2.3.0.7)
83
+ ast (~> 2.2)
84
+ powerpack (0.1.1)
85
+ pry (0.10.3)
86
+ coderay (~> 1.1.0)
87
+ method_source (~> 0.8.1)
88
+ slop (~> 3.4)
89
+ pry-rails (0.3.4)
90
+ pry (>= 0.9.10)
91
+ rack (1.6.4)
92
+ rack-test (0.6.3)
93
+ rack (>= 1.0)
94
+ rails (4.2.6)
95
+ actionmailer (= 4.2.6)
96
+ actionpack (= 4.2.6)
97
+ actionview (= 4.2.6)
98
+ activejob (= 4.2.6)
99
+ activemodel (= 4.2.6)
100
+ activerecord (= 4.2.6)
101
+ activesupport (= 4.2.6)
102
+ bundler (>= 1.3.0, < 2.0)
103
+ railties (= 4.2.6)
104
+ sprockets-rails
105
+ rails-deprecated_sanitizer (1.0.3)
106
+ activesupport (>= 4.2.0.alpha)
107
+ rails-dom-testing (1.0.7)
108
+ activesupport (>= 4.2.0.beta, < 5.0)
109
+ nokogiri (~> 1.6.0)
110
+ rails-deprecated_sanitizer (>= 1.0.1)
111
+ rails-html-sanitizer (1.0.3)
112
+ loofah (~> 2.0)
113
+ railties (4.2.6)
114
+ actionpack (= 4.2.6)
115
+ activesupport (= 4.2.6)
116
+ rake (>= 0.8.7)
117
+ thor (>= 0.18.1, < 2.0)
118
+ rainbow (2.1.0)
119
+ rake (10.5.0)
120
+ rspec (0.9.4)
121
+ rspec-core (3.4.4)
122
+ rspec-support (~> 3.4.0)
123
+ rspec-expectations (3.4.0)
124
+ diff-lcs (>= 1.2.0, < 2.0)
125
+ rspec-support (~> 3.4.0)
126
+ rspec-mocks (3.4.1)
127
+ diff-lcs (>= 1.2.0, < 2.0)
128
+ rspec-support (~> 3.4.0)
129
+ rspec-rails (3.4.2)
130
+ actionpack (>= 3.0, < 4.3)
131
+ activesupport (>= 3.0, < 4.3)
132
+ railties (>= 3.0, < 4.3)
133
+ rspec-core (~> 3.4.0)
134
+ rspec-expectations (~> 3.4.0)
135
+ rspec-mocks (~> 3.4.0)
136
+ rspec-support (~> 3.4.0)
137
+ rspec-support (3.4.1)
138
+ rubocop (0.39.0)
139
+ parser (>= 2.3.0.7, < 3.0)
140
+ powerpack (~> 0.1)
141
+ rainbow (>= 1.99.1, < 3.0)
142
+ ruby-progressbar (~> 1.7)
143
+ unicode-display_width (~> 1.0, >= 1.0.1)
144
+ ruby-progressbar (1.7.5)
145
+ shoulda-matchers (3.1.1)
146
+ activesupport (>= 4.0.0)
147
+ simplecov (0.11.2)
148
+ docile (~> 1.1.0)
149
+ json (~> 1.8)
150
+ simplecov-html (~> 0.10.0)
151
+ simplecov-html (0.10.0)
152
+ slop (3.6.0)
153
+ sprockets (3.6.0)
154
+ concurrent-ruby (~> 1.0)
155
+ rack (> 1, < 3)
156
+ sprockets-rails (3.0.4)
157
+ actionpack (>= 4.0)
158
+ activesupport (>= 4.0)
159
+ sprockets (>= 3.0.0)
160
+ sqlite3 (1.3.11)
161
+ thor (0.19.1)
162
+ thread_safe (0.3.5)
163
+ tzinfo (1.2.2)
164
+ thread_safe (~> 0.1)
165
+ unicode-display_width (1.0.3)
166
+ with_advisory_lock (3.0.0)
167
+ activerecord (>= 3.2)
168
+ thread_safe
169
+
170
+ PLATFORMS
171
+ ruby
172
+
173
+ DEPENDENCIES
174
+ bundler (~> 1.10)
175
+ configurations (~> 2.2.0)
176
+ database_cleaner (= 1.4.1)
177
+ factory_girl_rails (= 4.5.0)
178
+ ffaker (= 2.1.0)
179
+ monarchy!
180
+ pry-rails (= 0.3.4)
181
+ rake (~> 10.0)
182
+ rspec (~> 0)
183
+ rspec-rails
184
+ rubocop
185
+ shoulda-matchers
186
+ simplecov
187
+ sqlite3
188
+
189
+ BUNDLED WITH
190
+ 1.12.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Maciej Kwaśniak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Maciej Kwaśniak
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ [![Build Status](https://travis-ci.org/Exelord/Monarchy.svg?branch=master)](https://travis-ci.org/Exelord/Monarchy)
2
+ ![Monarchy Logo](monarchy.png?raw=true)
3
+
4
+ ## Documentation
5
+ There is existing fully documentation for that project in our Wiki. Take a look at [Monarchy Documentation] (https://github.com/Exelord/Monarchy/wiki)
6
+
7
+ ## Contributing
8
+
9
+ Bug reports and pull requests are welcome on GitHub at https://github.com/exelord/Monarchy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
10
+
11
+ ## Development
12
+ Gem dependencies:
13
+ - https://github.com/mceachen/closure_tree
14
+ - https://github.com/beatrichartz/configurations
15
+
16
+ ## License
17
+
18
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ class Monarchy::Hierarchy < ActiveRecord::Base
3
+ self.table_name = 'monarchy_hierarchies'
4
+ acts_as_hierarchy
5
+ end
data/bin/console ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'monarchy'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ require 'pry'
12
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators/active_record'
3
+
4
+ module Monarchy
5
+ class ResourceGenerator < Rails::Generators::NamedBase
6
+ include Rails::Generators::Migration
7
+
8
+ desc 'This generator creates a resourcify model'
9
+ source_root File.expand_path('../../templates', __FILE__)
10
+
11
+ def create_resource_file
12
+ template 'models/resource.rb', "app/models/#{file_name}.rb"
13
+ migration_template 'migrations/resource.rb', "db/migrate/create_#{file_name}_resource.rb"
14
+ end
15
+
16
+ def self.next_migration_number(dirname)
17
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators/active_record'
3
+
4
+ module Monarchy
5
+ class SetupGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ desc 'This generator setups Monarchy'
9
+ source_root File.expand_path('../../templates', __FILE__)
10
+
11
+ def setup_monarchy
12
+ template 'config.rb', 'config/initializers/monarchy.rb'
13
+ template 'models/user.rb', 'app/models/user.rb'
14
+ template 'models/hierarchy.rb', 'app/models/monarchy/hierarchy.rb'
15
+
16
+ migration_template 'migrations/hierarchy.rb', 'db/migrate/monarchy_create_hierarchies.rb'
17
+ migration_template 'migrations/membership.rb', 'db/migrate/monarchy_create_memberships.rb'
18
+ migration_template 'migrations/user.rb', 'db/migrate/monarchy_create_users.rb'
19
+ end
20
+
21
+ def self.next_migration_number(dirname)
22
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ Monarchy.configure do |config|
2
+ config.default_role.name = :guest
3
+ config.default_role.level = 0
4
+ config.default_role.inherited = false
5
+ end
@@ -0,0 +1,23 @@
1
+ class MonarchyCreateHierarchies < ActiveRecord::Migration
2
+ def change
3
+ create_table :monarchy_hierarchies do |t|
4
+ t.integer :parent_id
5
+ t.integer :resource_id, null: false
6
+ t.string :resource_type, null: false
7
+ t.timestamps null: false
8
+ end
9
+
10
+
11
+ create_table :monarchy_hierarchy_hierarchies, id: false do |t|
12
+ t.integer :ancestor_id, null: false
13
+ t.integer :descendant_id, null: false
14
+ t.integer :generations, null: false
15
+ end
16
+
17
+ add_index :monarchy_hierarchy_hierarchies, [:ancestor_id, :descendant_id, :generations],
18
+ unique: true,
19
+ name: 'hierarchy_anc_desc_idx'
20
+
21
+ add_index :monarchy_hierarchy_hierarchies, [:descendant_id], name: 'hierarchy_desc_idx'
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ class MonarchyCreateMemberships < ActiveRecord::Migration
2
+ def change
3
+ create_table :monarchy_members do |t|
4
+ t.belongs_to :user
5
+ t.belongs_to :hierarchy
6
+ t.timestamps null: false
7
+ end
8
+
9
+ create_table :monarchy_roles do |t|
10
+ t.string :name, null: false
11
+ t.integer :level, default: 0, null: false
12
+ t.boolean :inherited, default: false, null: false
13
+ t.timestamps null: false
14
+ end
15
+
16
+ add_index :monarchy_roles, :name, unique: true
17
+
18
+ create_table :monarchy_members_roles do |t|
19
+ t.belongs_to :role
20
+ t.belongs_to :member
21
+ t.timestamps null: false
22
+ end
23
+
24
+ add_index :monarchy_members_roles, [:role_id, :member_id], unique: true
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ class Create<%= class_name %>Resource < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%= file_name.pluralize %> do |t|
4
+ t.string :name
5
+ t.timestamps null: false
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ class MonarchyCreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.timestamps null: false
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ class Monarchy::Hierarchy < ActiveRecord::Base
2
+ self.table_name = 'monarchy_hierarchies'
3
+ acts_as_hierarchy
4
+ end
@@ -0,0 +1,3 @@
1
+ class <%= class_name %> < ActiveRecord::Base
2
+ acts_as_resource
3
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ acts_as_user
3
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module Monarchy
3
+ module ActsAsHierarchy
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def acts_as_hierarchy
8
+ has_closure_tree dependent: :destroy
9
+
10
+ has_many :members, class_name: 'Monarchy::Member'
11
+ belongs_to :resource, polymorphic: true, dependent: :destroy
12
+
13
+ validates :resource, presence: true
14
+
15
+ include Monarchy::ActsAsHierarchy::InstanceMethods
16
+ end
17
+ end
18
+
19
+ module InstanceMethods
20
+ def memberless_ancestors_for(user)
21
+ ancestors.joins('LEFT JOIN monarchy_members on monarchy_hierarchies.id = monarchy_members.hierarchy_id')
22
+ .where("monarchy_members.user_id != #{user.id} OR monarchy_members.id IS NULL")
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ ActiveRecord::Base.send :include, Monarchy::ActsAsHierarchy
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+ module Monarchy
3
+ module ActsAsResource
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def acts_as_resource(options = {})
8
+ parent_as(options[:parent_as]) if options[:parent_as]
9
+
10
+ after_create :ensure_hierarchy
11
+
12
+ has_many :members, through: :hierarchy
13
+ has_one :hierarchy, as: :resource, dependent: :destroy, class_name: 'Monarchy::Hierarchy'
14
+
15
+ include_scopes
16
+
17
+ include Monarchy::ActsAsResource::InstanceMethods
18
+ end
19
+
20
+ private
21
+
22
+ def parent_as(name)
23
+ define_method "#{name}=" do |value|
24
+ super(value)
25
+ self.parent = value
26
+ end
27
+ end
28
+
29
+ # rubocop:disable MethodLength
30
+ def include_scopes
31
+ scope :in, (lambda do |resource|
32
+ joins(:hierarchy).where(monarchy_hierarchies: { parent_id: resource.hierarchy.id })
33
+ end)
34
+
35
+ scope :accessible_for, (lambda do |user|
36
+ joins(:hierarchy)
37
+ .joins('INNER JOIN "monarchy_hierarchy_hierarchies" ON '\
38
+ '"monarchy_hierarchies"."id" = "monarchy_hierarchy_hierarchies"."ancestor_id"')
39
+ .joins('INNER JOIN "monarchy_members" ON '\
40
+ '"monarchy_members"."hierarchy_id" = "monarchy_hierarchy_hierarchies"."descendant_id"')
41
+ .where(monarchy_members: { user_id: user.id }).uniq
42
+ end)
43
+ end
44
+ end
45
+ # rubocop:enable MethodLength
46
+
47
+ module InstanceMethods
48
+ def parent
49
+ @parent = hierarchy.try(:parent).try(:resource) || @parent
50
+ end
51
+
52
+ def parent=(resource)
53
+ if hierarchy
54
+ hierarchy.update(parent: resource.try(:hierarchy))
55
+ else
56
+ @parent = resource
57
+ end
58
+ end
59
+
60
+ def children
61
+ @children ||= children_resources
62
+ end
63
+
64
+ def children=(array)
65
+ hierarchy.update(children: hierarchies_for(array)) if hierarchy
66
+
67
+ @children = array
68
+ end
69
+
70
+ private
71
+
72
+ def ensure_hierarchy
73
+ self.hierarchy ||= Monarchy::Hierarchy.create(
74
+ resource: self,
75
+ parent: parent.try(:hierarchy),
76
+ children: hierarchies_for(children)
77
+ )
78
+ end
79
+
80
+ def children_resources
81
+ c = hierarchy.try(:children)
82
+ return nil if c.nil?
83
+ c.includes(:resource).map(&:resource)
84
+ end
85
+
86
+ def hierarchies_for(array)
87
+ Array(array).map(&:hierarchy)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ ActiveRecord::Base.send :include, Monarchy::ActsAsResource
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+ module Monarchy
3
+ module ActsAsUser
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def acts_as_user
8
+ has_many :members, class_name: 'Monarchy::Member'
9
+
10
+ include Monarchy::ActsAsUser::InstanceMethods
11
+ end
12
+ end
13
+
14
+ module InstanceMethods
15
+ def role_for(resource)
16
+ ancestors_ids = resource.hierarchy.self_and_ancestors_ids
17
+ Monarchy::Role.joins(:members)
18
+ .where("((monarchy_roles.inherited = 't' "\
19
+ "AND monarchy_members.hierarchy_id IN (#{ancestors_ids.join(',')})) "\
20
+ "OR (monarchy_members.hierarchy_id = #{resource.hierarchy.id})) "\
21
+ "AND monarchy_members.user_id = #{id}")
22
+ .order(level: :desc).first
23
+ end
24
+
25
+ def grant(role_name, resource)
26
+ ActiveRecord::Base.transaction do
27
+ Monarchy::Member.create(build_members(resource.hierarchy.memberless_ancestors_for(self)))
28
+ grant_or_create_member(role_name, resource)
29
+ end
30
+ end
31
+
32
+ def member_for(resource)
33
+ resource.hierarchy.members.where("monarchy_members.user_id": id).first
34
+ end
35
+
36
+ def revoke_access(resource)
37
+ self_and_descendant_ids = resource.hierarchy.self_and_descendant_ids
38
+
39
+ ActiveRecord::Base.transaction do
40
+ members_roles_for(self_and_descendant_ids).destroy_all
41
+ try_revoke_ancestors_for(resource)
42
+ end
43
+ end
44
+
45
+ def revoke_role(role_name, resource)
46
+ members_roles = member_for(resource).members_roles
47
+
48
+ if only_this_role(members_roles, role_name)
49
+ revoke_access(resource)
50
+ else
51
+ members_roles.joins(:role).where("monarchy_roles.name": role_name).destroy_all
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def grant_or_create_member(role_name, resource)
58
+ role = Monarchy::Role.find_by(name: role_name)
59
+ member = member_for(resource)
60
+
61
+ if member
62
+ Monarchy::MembersRole.create(member: member, role: role)
63
+ else
64
+ member = Monarchy::Member.create(build_members(resource.hierarchy, [role])).first
65
+ end
66
+
67
+ member
68
+ end
69
+
70
+ def build_members(hierarchies, roles = [])
71
+ Array(hierarchies).map { |hierarchy| { user: self, hierarchy: hierarchy, roles: roles } }
72
+ end
73
+
74
+ def equal_role_name?(role, role_name)
75
+ role.name == role_name.to_s
76
+ end
77
+
78
+ def only_this_role(members_roles, role_name = nil)
79
+ role_name ||= Monarchy.configuration.default_role.name.to_s
80
+ members_roles.count == 1 && equal_role_name?(members_roles.first.role, role_name)
81
+ end
82
+
83
+ def members_roles_for(hierarchy_ids)
84
+ Monarchy::MembersRole.joins(:member)
85
+ .where(monarchy_members: { hierarchy_id: hierarchy_ids, user_id: id })
86
+ end
87
+
88
+ def try_revoke_ancestors_for(resource)
89
+ resource.hierarchy.ancestors.each do |hierarchy|
90
+ members_roles = members_roles_for(hierarchy.self_and_descendant_ids)
91
+ only_this_role(members_roles) ? members_roles.destroy_all : break
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ ActiveRecord::Base.send :include, Monarchy::ActsAsUser
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module Monarchy
3
+ class Engine < Rails::Engine
4
+ isolate_namespace Monarchy
5
+ end
6
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ module Monarchy
3
+ class Member < ActiveRecord::Base
4
+ self.table_name = 'monarchy_members'
5
+
6
+ has_many :members_roles, dependent: :destroy
7
+ has_many :roles, through: :members_roles
8
+
9
+ belongs_to :user
10
+ belongs_to :hierarchy
11
+
12
+ delegate :resource, to: :hierarchy
13
+ delegate :resource=, to: :hierarchy
14
+
15
+ validates :user_id, uniqueness: { scope: :hierarchy_id }
16
+ validates :user, presence: true
17
+ validates :hierarchy, presence: true
18
+
19
+ before_create :set_default_role
20
+
21
+ private
22
+
23
+ def set_default_role
24
+ roles = self.roles
25
+ roles << Monarchy::Role.find_or_create_by(
26
+ name: Monarchy.configuration.default_role.name,
27
+ inherited: Monarchy.configuration.default_role.inherited,
28
+ level: Monarchy.configuration.default_role.level)
29
+ self.roles = roles.uniq
30
+ end
31
+ end
32
+
33
+ class Role < ActiveRecord::Base
34
+ self.table_name = 'monarchy_roles'
35
+
36
+ has_many :members_roles, dependent: :destroy
37
+ has_many :members, through: :members_roles
38
+ end
39
+
40
+ class MembersRole < ActiveRecord::Base
41
+ self.table_name = 'monarchy_members_roles'
42
+
43
+ belongs_to :member
44
+ belongs_to :role
45
+
46
+ validates :role_id, uniqueness: { scope: :member_id }
47
+ end
48
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Monarchy
3
+ VERSION = '0.2.1'
4
+ end
data/lib/monarchy.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ require 'closure_tree'
3
+ require 'configurations'
4
+
5
+ require 'monarchy/acts_as_hierarchy'
6
+ require 'monarchy/acts_as_resource'
7
+ require 'monarchy/acts_as_user'
8
+ require 'monarchy/membership'
9
+ require "monarchy/engine"
10
+
11
+ module Monarchy
12
+ include Configurations
13
+
14
+ not_configured do |prop|
15
+ raise NoMethodError, "#{prop} must be configured"
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # desc "Explaining what the task does"
3
+ # task :monarchy do
4
+ # # Task goes here
5
+ # end
data/monarchy.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'monarchy/version'
6
+
7
+ # Describe your gem and declare its dependencies:
8
+ Gem::Specification.new do |s|
9
+ s.name = 'monarchy'
10
+ s.version = Monarchy::VERSION
11
+ s.authors = ['Exelord']
12
+ s.email = ['']
13
+ s.homepage = 'https://github.com/Exelord/Monarchy'
14
+ s.summary = 'Hierarchical access management system with roles inheritance.'
15
+ s.description = s.summary
16
+ s.license = 'MIT'
17
+
18
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ s.test_files = Dir['test/**/*']
20
+
21
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless s.respond_to?(:metadata)
22
+ s.metadata['allowed_push_host'] = 'https://rubygems.org'
23
+
24
+ s.bindir = 'exe'
25
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ s.require_paths = ['lib']
27
+
28
+ s.add_runtime_dependency 'rails', '~> 4.2', '>= 4.2.4'
29
+ s.add_dependency 'closure_tree', '6.0.0'
30
+ s.add_dependency 'configurations', '2.2.0'
31
+
32
+ s.add_development_dependency 'bundler', '~> 1.10'
33
+ s.add_development_dependency 'rake', '~> 10.0'
34
+ s.add_development_dependency 'rspec', '~> 0'
35
+ end
data/monarchy.png ADDED
Binary file
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monarchy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Exelord
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-02 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'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 4.2.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '4.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 4.2.4
33
+ - !ruby/object:Gem::Dependency
34
+ name: closure_tree
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '='
38
+ - !ruby/object:Gem::Version
39
+ version: 6.0.0
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '='
45
+ - !ruby/object:Gem::Version
46
+ version: 6.0.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: configurations
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 2.2.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '='
59
+ - !ruby/object:Gem::Version
60
+ version: 2.2.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.10'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.10'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '10.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '10.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ description: Hierarchical access management system with roles inheritance.
104
+ email:
105
+ - ''
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - ".gitignore"
111
+ - ".lock-ClosureTreeMonarchyHierarchy-1609118677"
112
+ - ".rspec"
113
+ - ".rubocop.yml"
114
+ - ".travis.yml"
115
+ - CODE_OF_CONDUCT.md
116
+ - Gemfile
117
+ - Gemfile.lock
118
+ - LICENSE.txt
119
+ - MIT-LICENSE
120
+ - README.md
121
+ - Rakefile
122
+ - app/models/monarchy/hierarchy.rb
123
+ - bin/console
124
+ - bin/setup
125
+ - lib/generators/monarchy/resource_generator.rb
126
+ - lib/generators/monarchy/setup_generator.rb
127
+ - lib/generators/templates/config.rb
128
+ - lib/generators/templates/migrations/hierarchy.rb
129
+ - lib/generators/templates/migrations/membership.rb
130
+ - lib/generators/templates/migrations/resource.rb
131
+ - lib/generators/templates/migrations/user.rb
132
+ - lib/generators/templates/models/hierarchy.rb
133
+ - lib/generators/templates/models/resource.rb
134
+ - lib/generators/templates/models/user.rb
135
+ - lib/monarchy.rb
136
+ - lib/monarchy/acts_as_hierarchy.rb
137
+ - lib/monarchy/acts_as_resource.rb
138
+ - lib/monarchy/acts_as_user.rb
139
+ - lib/monarchy/engine.rb
140
+ - lib/monarchy/membership.rb
141
+ - lib/monarchy/version.rb
142
+ - lib/tasks/monarchy_tasks.rake
143
+ - monarchy.gemspec
144
+ - monarchy.png
145
+ homepage: https://github.com/Exelord/Monarchy
146
+ licenses:
147
+ - MIT
148
+ metadata:
149
+ allowed_push_host: https://rubygems.org
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 2.5.1
167
+ signing_key:
168
+ specification_version: 4
169
+ summary: Hierarchical access management system with roles inheritance.
170
+ test_files: []