strongbolt 0.3.7 → 0.3.8
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 +4 -4
- data/.ruby-version +1 -1
- data/Gemfile.lock +52 -54
- data/README.md +4 -0
- data/app/controllers/strongbolt/security_controller.rb +2 -2
- data/app/controllers/strongbolt/user_groups_users_controller.rb +4 -4
- data/app/views/strongbolt/roles/_capabilities.html.erb +4 -4
- data/app/views/strongbolt/roles/_capability.html.erb +3 -3
- data/app/views/strongbolt/roles/_form.html.erb +1 -1
- data/lib/generators/strongbolt/install_generator.rb +1 -1
- data/lib/strongbolt/bolted.rb +4 -4
- data/lib/strongbolt/capabilities_role.rb +2 -2
- data/lib/strongbolt/capability.rb +3 -5
- data/lib/strongbolt/configuration.rb +1 -1
- data/lib/strongbolt/engine.rb +1 -1
- data/lib/strongbolt/role.rb +2 -4
- data/lib/strongbolt/roles_user_group.rb +2 -2
- data/lib/strongbolt/tenantable.rb +25 -13
- data/lib/strongbolt/user_abilities.rb +22 -22
- data/lib/strongbolt/user_group.rb +1 -3
- data/lib/strongbolt/user_groups_user.rb +2 -2
- data/lib/strongbolt/users_tenant.rb +0 -2
- data/lib/strongbolt/version.rb +1 -1
- data/lib/tasks/strongbolt_tasks.rake +7 -7
- data/spec/spec_helper.rb +1 -1
- data/spec/strongbolt/configuration_spec.rb +10 -10
- data/strongbolt.gemspec +2 -2
- metadata +10 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c67e76eb347b2f56147ce3ea4edb40c0abbec16c
|
|
4
|
+
data.tar.gz: fe67b27504d1db3bcd74cd861aee53b979dcbc84
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b456dbe25f2c8fa8a2492cff18a864b1e21f76b3b38fe9427da888d06c17e6cbab82778863d37f5e68cb878e2f3e7599abf1f8f9d3c3e15538ec4cdaeebc3cdd
|
|
7
|
+
data.tar.gz: f077745183044f7feb443bda2dc0d4e438b7597ad7034ed7f089a6eb088d835bf1de72c293bec8c255867a52360b6df96c630e35a4a220d18594e2845ad551b5
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ruby-2.1
|
|
1
|
+
ruby-2.3.1
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
strongbolt (0.3.
|
|
4
|
+
strongbolt (0.3.7)
|
|
5
5
|
awesome_nested_set (~> 3.0.0)
|
|
6
6
|
grant (~> 3.0)
|
|
7
7
|
simple_form (~> 3.0)
|
|
@@ -9,27 +9,27 @@ PATH
|
|
|
9
9
|
GEM
|
|
10
10
|
remote: https://rubygems.org/
|
|
11
11
|
specs:
|
|
12
|
-
actionmailer (4.1.
|
|
13
|
-
actionpack (= 4.1.
|
|
14
|
-
actionview (= 4.1.
|
|
12
|
+
actionmailer (4.1.15)
|
|
13
|
+
actionpack (= 4.1.15)
|
|
14
|
+
actionview (= 4.1.15)
|
|
15
15
|
mail (~> 2.5, >= 2.5.4)
|
|
16
|
-
actionpack (4.1.
|
|
17
|
-
actionview (= 4.1.
|
|
18
|
-
activesupport (= 4.1.
|
|
16
|
+
actionpack (4.1.15)
|
|
17
|
+
actionview (= 4.1.15)
|
|
18
|
+
activesupport (= 4.1.15)
|
|
19
19
|
rack (~> 1.5.2)
|
|
20
20
|
rack-test (~> 0.6.2)
|
|
21
|
-
actionview (4.1.
|
|
22
|
-
activesupport (= 4.1.
|
|
21
|
+
actionview (4.1.15)
|
|
22
|
+
activesupport (= 4.1.15)
|
|
23
23
|
builder (~> 3.1)
|
|
24
24
|
erubis (~> 2.7.0)
|
|
25
|
-
activemodel (4.1.
|
|
26
|
-
activesupport (= 4.1.
|
|
25
|
+
activemodel (4.1.15)
|
|
26
|
+
activesupport (= 4.1.15)
|
|
27
27
|
builder (~> 3.1)
|
|
28
|
-
activerecord (4.1.
|
|
29
|
-
activemodel (= 4.1.
|
|
30
|
-
activesupport (= 4.1.
|
|
28
|
+
activerecord (4.1.15)
|
|
29
|
+
activemodel (= 4.1.15)
|
|
30
|
+
activesupport (= 4.1.15)
|
|
31
31
|
arel (~> 5.0.0)
|
|
32
|
-
activesupport (4.1.
|
|
32
|
+
activesupport (4.1.15)
|
|
33
33
|
i18n (~> 0.6, >= 0.6.9)
|
|
34
34
|
json (~> 1.7, >= 1.7.7)
|
|
35
35
|
minitest (~> 5.1)
|
|
@@ -39,51 +39,52 @@ GEM
|
|
|
39
39
|
awesome_nested_set (3.0.3)
|
|
40
40
|
activerecord (>= 4.0.0, < 5)
|
|
41
41
|
builder (3.2.2)
|
|
42
|
+
concurrent-ruby (1.0.2)
|
|
42
43
|
diff-lcs (1.2.5)
|
|
43
44
|
erubis (2.7.0)
|
|
44
|
-
fabrication (2.
|
|
45
|
+
fabrication (2.15.2)
|
|
45
46
|
fuubar (2.0.0)
|
|
46
47
|
rspec (~> 3.0)
|
|
47
48
|
ruby-progressbar (~> 1.4)
|
|
48
49
|
grant (3.0.0)
|
|
49
50
|
activerecord (>= 4.0.0)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
rack (1.5.
|
|
59
|
-
rack-test (0.6.
|
|
51
|
+
i18n (0.7.0)
|
|
52
|
+
json (1.8.3)
|
|
53
|
+
mail (2.6.4)
|
|
54
|
+
mime-types (>= 1.16, < 4)
|
|
55
|
+
mime-types (3.0)
|
|
56
|
+
mime-types-data (~> 3.2015)
|
|
57
|
+
mime-types-data (3.2016.0221)
|
|
58
|
+
minitest (5.8.4)
|
|
59
|
+
rack (1.5.5)
|
|
60
|
+
rack-test (0.6.3)
|
|
60
61
|
rack (>= 1.0)
|
|
61
|
-
rails (4.1.
|
|
62
|
-
actionmailer (= 4.1.
|
|
63
|
-
actionpack (= 4.1.
|
|
64
|
-
actionview (= 4.1.
|
|
65
|
-
activemodel (= 4.1.
|
|
66
|
-
activerecord (= 4.1.
|
|
67
|
-
activesupport (= 4.1.
|
|
62
|
+
rails (4.1.15)
|
|
63
|
+
actionmailer (= 4.1.15)
|
|
64
|
+
actionpack (= 4.1.15)
|
|
65
|
+
actionview (= 4.1.15)
|
|
66
|
+
activemodel (= 4.1.15)
|
|
67
|
+
activerecord (= 4.1.15)
|
|
68
|
+
activesupport (= 4.1.15)
|
|
68
69
|
bundler (>= 1.3.0, < 2.0)
|
|
69
|
-
railties (= 4.1.
|
|
70
|
+
railties (= 4.1.15)
|
|
70
71
|
sprockets-rails (~> 2.0)
|
|
71
|
-
railties (4.1.
|
|
72
|
-
actionpack (= 4.1.
|
|
73
|
-
activesupport (= 4.1.
|
|
72
|
+
railties (4.1.15)
|
|
73
|
+
actionpack (= 4.1.15)
|
|
74
|
+
activesupport (= 4.1.15)
|
|
74
75
|
rake (>= 0.8.7)
|
|
75
76
|
thor (>= 0.18.1, < 2.0)
|
|
76
|
-
rake (
|
|
77
|
+
rake (11.1.2)
|
|
77
78
|
rspec (3.1.0)
|
|
78
79
|
rspec-core (~> 3.1.0)
|
|
79
80
|
rspec-expectations (~> 3.1.0)
|
|
80
81
|
rspec-mocks (~> 3.1.0)
|
|
81
|
-
rspec-core (3.1.
|
|
82
|
+
rspec-core (3.1.7)
|
|
82
83
|
rspec-support (~> 3.1.0)
|
|
83
84
|
rspec-expectations (3.1.2)
|
|
84
85
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
85
86
|
rspec-support (~> 3.1.0)
|
|
86
|
-
rspec-mocks (3.1.
|
|
87
|
+
rspec-mocks (3.1.3)
|
|
87
88
|
rspec-support (~> 3.1.0)
|
|
88
89
|
rspec-rails (3.1.0)
|
|
89
90
|
actionpack (>= 3.0)
|
|
@@ -93,26 +94,23 @@ GEM
|
|
|
93
94
|
rspec-expectations (~> 3.1.0)
|
|
94
95
|
rspec-mocks (~> 3.1.0)
|
|
95
96
|
rspec-support (~> 3.1.0)
|
|
96
|
-
rspec-support (3.1.
|
|
97
|
-
ruby-progressbar (1.
|
|
97
|
+
rspec-support (3.1.2)
|
|
98
|
+
ruby-progressbar (1.8.1)
|
|
98
99
|
shoulda-matchers (2.7.0)
|
|
99
100
|
activesupport (>= 3.0.0)
|
|
100
101
|
simple_form (3.2.1)
|
|
101
102
|
actionpack (> 4, < 5.1)
|
|
102
103
|
activemodel (> 4, < 5.1)
|
|
103
|
-
sprockets (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
tilt (~> 1.1, != 1.3.0)
|
|
108
|
-
sprockets-rails (2.2.2)
|
|
104
|
+
sprockets (3.6.0)
|
|
105
|
+
concurrent-ruby (~> 1.0)
|
|
106
|
+
rack (> 1, < 3)
|
|
107
|
+
sprockets-rails (2.3.3)
|
|
109
108
|
actionpack (>= 3.0)
|
|
110
109
|
activesupport (>= 3.0)
|
|
111
110
|
sprockets (>= 2.8, < 4.0)
|
|
112
|
-
sqlite3 (1.3.
|
|
111
|
+
sqlite3 (1.3.11)
|
|
113
112
|
thor (0.19.1)
|
|
114
|
-
thread_safe (0.3.
|
|
115
|
-
tilt (1.4.1)
|
|
113
|
+
thread_safe (0.3.5)
|
|
116
114
|
tzinfo (1.2.2)
|
|
117
115
|
thread_safe (~> 0.1)
|
|
118
116
|
|
|
@@ -126,9 +124,9 @@ DEPENDENCIES
|
|
|
126
124
|
rails (~> 4.1.0)
|
|
127
125
|
rake
|
|
128
126
|
rspec-rails (~> 3.1.0)
|
|
129
|
-
shoulda-matchers
|
|
130
|
-
sqlite3
|
|
127
|
+
shoulda-matchers (~> 2.7.0)
|
|
128
|
+
sqlite3
|
|
131
129
|
strongbolt!
|
|
132
130
|
|
|
133
131
|
BUNDLED WITH
|
|
134
|
-
1.
|
|
132
|
+
1.12.3
|
data/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
RBAC framework for model-level authorization gem with very granular control on permissions, using Capabilities, Roles and UserGroups.
|
|
4
4
|
|
|
5
|
+
Checkout our [minimal example app](https://github.com/AnalyticsMediaGroup/strongbolt_example) to try it out without the need to set it up yourself.
|
|
6
|
+
|
|
5
7
|
Only works with Rails 4.
|
|
6
8
|
|
|
7
9
|
## Installation
|
|
@@ -30,6 +32,8 @@ If you plan on using the built-in view to manage user groups, roles and permissi
|
|
|
30
32
|
|
|
31
33
|
You will need to have jQuery or a similar library with Ajax to make it work
|
|
32
34
|
|
|
35
|
+
There is a minimal example app using Strongbolt made available [here](https://github.com/AnalyticsMediaGroup/strongbolt_example). If you experience any issues setting Strongbolt up or configuring it, this example app is already setup for you and might be able to resolve some issues.
|
|
36
|
+
|
|
33
37
|
## Usage
|
|
34
38
|
|
|
35
39
|
### Configuration
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module Strongbolt
|
|
2
2
|
class UserGroupsUsersController < ::StrongboltController
|
|
3
3
|
|
|
4
|
-
self.model_for_authorization = "UserGroup"
|
|
4
|
+
self.model_for_authorization = "Strongbolt::UserGroup"
|
|
5
5
|
|
|
6
6
|
def create
|
|
7
7
|
@user_group = UserGroup.find(params[:user_group_id])
|
|
8
8
|
@user = Strongbolt.user_class_constant.find(params[:id])
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
@user_group.users << @user unless @user_group.users.include?(@user)
|
|
11
11
|
|
|
12
12
|
redirect_to request.referrer || user_group_path(@user_group)
|
|
@@ -15,7 +15,7 @@ module Strongbolt
|
|
|
15
15
|
def destroy
|
|
16
16
|
@user_group = UserGroup.find(params[:user_group_id])
|
|
17
17
|
@user = Strongbolt.user_class_constant.find(params[:id])
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
@user_group.users.delete @user
|
|
20
20
|
|
|
21
21
|
redirect_to request.referrer || user_group_path(@user_group)
|
|
@@ -32,4 +32,4 @@ module Strongbolt
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
end
|
|
35
|
-
end
|
|
35
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<%= simple_form_for(Capability.new, url: role_capabilities_path(@role), as: :capability) do |f| %>
|
|
1
|
+
<%= simple_form_for(Strongbolt::Capability.new, url: role_capabilities_path(@role), as: :capability) do |f| %>
|
|
2
2
|
<table class="table table-striped table-condensed" id="role-capabilities" data-url="<%= role_capabilities_path(@role) %>">
|
|
3
3
|
|
|
4
4
|
<thead>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
<tbody>
|
|
14
14
|
<% @keys.each do |key| %>
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
<tr>
|
|
17
17
|
<td><%= key[:model] %></td>
|
|
18
18
|
<td>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
<tfoot>
|
|
34
34
|
<tr>
|
|
35
|
-
<td><%= f.input_field :model, collection: Capability.models, include_blank: false %></td>
|
|
35
|
+
<td><%= f.input_field :model, collection: Strongbolt::Capability.models, include_blank: false %></td>
|
|
36
36
|
<td><%= f.input_field :require_ownership, as: :select, include_blank: false %></td>
|
|
37
37
|
<td><%= f.input_field :require_tenant_access, as: :select, include_blank: false %></td>
|
|
38
38
|
<td colspan="4">
|
|
@@ -44,4 +44,4 @@
|
|
|
44
44
|
|
|
45
45
|
</table>
|
|
46
46
|
|
|
47
|
-
<% end %>
|
|
47
|
+
<% end %>
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
data-model="<%= key[:model] %>"
|
|
3
3
|
data-require-ownership=<%= key[:require_ownership] %>
|
|
4
4
|
data-require-tenant-access=<%= key[:require_tenant_access] %>>
|
|
5
|
-
|
|
6
|
-
<% Capability::Actions.each do |action| %>
|
|
5
|
+
|
|
6
|
+
<% Strongbolt::Capability::Actions.each do |action| %>
|
|
7
7
|
<% if inherited_capability[action.to_sym] %>
|
|
8
8
|
<button type="button" disabled class="btn btn-xs btn-success" title="Inherited">
|
|
9
9
|
<%= action.capitalize %>
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
<% end %>
|
|
19
19
|
<% end %>
|
|
20
20
|
|
|
21
|
-
</div>
|
|
21
|
+
</div>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
method: params[:action].to_sym == :new ? :post : :put) do |f| %>
|
|
5
5
|
|
|
6
6
|
<%= f.input :name %>
|
|
7
|
-
<%= f.input :parent_id, collection: Role.where.not(id: @role.id).order('parent_id IS NOT NULL', :parent_id, :name) %>
|
|
7
|
+
<%= f.input :parent_id, collection: Strongbolt::Role.where.not(id: @role.id).order('parent_id IS NOT NULL', :parent_id, :name) %>
|
|
8
8
|
<%= f.input :description %>
|
|
9
9
|
|
|
10
10
|
<%= f.button :submit, class: 'btn-primary', value: 'Save' %>
|
data/lib/strongbolt/bolted.rb
CHANGED
|
@@ -73,7 +73,7 @@ module Strongbolt
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
end
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
module InstanceMethods
|
|
78
78
|
#
|
|
79
79
|
# Asks permission to performa an operation on the current instance
|
|
@@ -91,7 +91,7 @@ module Strongbolt
|
|
|
91
91
|
send self.class.owner_attribute
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
def self.included(receiver)
|
|
96
96
|
receiver.extend ClassMethods
|
|
97
97
|
receiver.send :include, InstanceMethods
|
|
@@ -115,11 +115,11 @@ module Strongbolt
|
|
|
115
115
|
unless granted
|
|
116
116
|
Strongbolt.access_denied user, instance, action, $request.try(:fullpath)
|
|
117
117
|
end
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
granted
|
|
120
120
|
end # End Grant
|
|
121
121
|
|
|
122
122
|
end
|
|
123
123
|
end
|
|
124
124
|
end
|
|
125
|
-
end
|
|
125
|
+
end
|
|
@@ -5,11 +5,11 @@ module Strongbolt
|
|
|
5
5
|
belongs_to :role,
|
|
6
6
|
:class_name => "Strongbolt::Role",
|
|
7
7
|
:inverse_of => :capabilities_roles
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
belongs_to :capability,
|
|
10
10
|
:class_name => "Strongbolt::Capability",
|
|
11
11
|
:inverse_of => :capabilities_roles
|
|
12
12
|
|
|
13
13
|
validates_presence_of :role, :capability
|
|
14
14
|
end
|
|
15
|
-
end
|
|
15
|
+
end
|
|
@@ -12,7 +12,7 @@ module Strongbolt
|
|
|
12
12
|
:class_name => "Strongbolt::CapabilitiesRole",
|
|
13
13
|
:dependent => :restrict_with_exception,
|
|
14
14
|
:inverse_of => :capability
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
has_many :roles, :through => :capabilities_roles
|
|
17
17
|
|
|
18
18
|
has_many :users, through: :roles
|
|
@@ -57,7 +57,7 @@ module Strongbolt
|
|
|
57
57
|
! (table.last[:model] == capability.model &&
|
|
58
58
|
table.last[:require_ownership] == capability.require_ownership &&
|
|
59
59
|
table.last[:require_tenant_access] == capability.require_tenant_access)
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
table << {
|
|
62
62
|
model: capability.model,
|
|
63
63
|
require_ownership: capability.require_ownership,
|
|
@@ -87,7 +87,7 @@ module Strongbolt
|
|
|
87
87
|
require_ownership: capability.require_ownership,
|
|
88
88
|
require_tenant_access: capability.require_tenant_access
|
|
89
89
|
}
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
hash[key] ||= {
|
|
92
92
|
find: false,
|
|
93
93
|
create: false,
|
|
@@ -161,5 +161,3 @@ module Strongbolt
|
|
|
161
161
|
end
|
|
162
162
|
end
|
|
163
163
|
end
|
|
164
|
-
|
|
165
|
-
Capability = Strongbolt::Capability unless defined? Capability
|
data/lib/strongbolt/engine.rb
CHANGED
data/lib/strongbolt/role.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Strongbolt
|
|
|
10
10
|
:dependent => :restrict_with_exception,
|
|
11
11
|
:inverse_of => :role
|
|
12
12
|
has_many :user_groups, :through => :roles_user_groups
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
has_many :users, through: :user_groups
|
|
15
15
|
|
|
16
16
|
has_many :capabilities_roles,
|
|
@@ -31,7 +31,7 @@ module Strongbolt
|
|
|
31
31
|
Strongbolt::Capability.joins(:roles)
|
|
32
32
|
.where("strongbolt_roles.lft < :lft AND strongbolt_roles.rgt > :rgt", lft: lft, rgt: rgt)
|
|
33
33
|
.distinct
|
|
34
|
-
|
|
34
|
+
end
|
|
35
35
|
|
|
36
36
|
private
|
|
37
37
|
|
|
@@ -42,5 +42,3 @@ module Strongbolt
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
|
-
|
|
46
|
-
Role = Strongbolt::Role unless defined? Role
|
|
@@ -5,11 +5,11 @@ module Strongbolt
|
|
|
5
5
|
belongs_to :user_group,
|
|
6
6
|
:class_name => "Strongbolt::UserGroup",
|
|
7
7
|
:inverse_of => :roles_user_groups
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
belongs_to :role,
|
|
10
10
|
:class_name => "Strongbolt::Role",
|
|
11
11
|
:inverse_of => :roles_user_groups
|
|
12
12
|
|
|
13
13
|
validates_presence_of :user_group, :role
|
|
14
14
|
end
|
|
15
|
-
end
|
|
15
|
+
end
|
|
@@ -91,30 +91,30 @@ module Strongbolt
|
|
|
91
91
|
else
|
|
92
92
|
raise DirectAssociationNotConfigured, "Class #{klass.name} is 1 degree from #{self.name} but the association isn't configured, you should implement it before using tenant method"
|
|
93
93
|
end
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
# The coming class has a relationship to the tenant
|
|
96
96
|
else
|
|
97
97
|
# If already created, we don't need to go further
|
|
98
98
|
return singular_association_name if klass.new.respond_to?(singular_association_name)
|
|
99
99
|
return plural_association_name if klass.new.respond_to?(plural_association_name)
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
# Inverse association
|
|
102
102
|
inverse = inverse_of(association)
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
# If no inverse, we cannot go further
|
|
105
105
|
if inverse.nil?
|
|
106
106
|
raise InverseAssociationNotConfigured, "Assocation #{association.name} on #{association.klass.name} could not be configured correctly as no inverse has been found"
|
|
107
107
|
elsif inverse.options.has_key? :polymorphic
|
|
108
108
|
return nil
|
|
109
109
|
end
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
|
|
112
112
|
# Common options
|
|
113
113
|
options = {
|
|
114
114
|
through: inverse.name,
|
|
115
115
|
autosave: false
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
# If the target is linked through some sort of has_many
|
|
119
119
|
if link == plural_association_name || inverse.collection?
|
|
120
120
|
# Has many
|
|
@@ -123,7 +123,7 @@ module Strongbolt
|
|
|
123
123
|
# Setup the scope with_name_of_plural_associations
|
|
124
124
|
# Current tenant table name
|
|
125
125
|
klass.has_many assoc, options
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
Strongbolt.logger.debug "#{klass.name} has_many #{plural_association_name} through: #{options[:through]}\n\n"
|
|
128
128
|
|
|
129
129
|
# Otherwise, it's linked through a has one
|
|
@@ -133,7 +133,7 @@ module Strongbolt
|
|
|
133
133
|
# Setup the association
|
|
134
134
|
# Setup the scope with_name_of_plural_associations
|
|
135
135
|
klass.has_one assoc, options
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
Strongbolt.logger.debug "#{klass.name} has_one #{singular_association_name} through: #{options[:through]}\n\n"
|
|
138
138
|
end
|
|
139
139
|
end
|
|
@@ -177,7 +177,7 @@ module Strongbolt
|
|
|
177
177
|
#
|
|
178
178
|
#
|
|
179
179
|
def create_users_tenant_subclass
|
|
180
|
-
unless Strongbolt.const_defined?(
|
|
180
|
+
unless Strongbolt.const_defined?(users_tenants_subclass_name)
|
|
181
181
|
users_tenant_subclass = Class.new(Strongbolt::UsersTenant)
|
|
182
182
|
users_tenant_subclass.class_eval <<-RUBY
|
|
183
183
|
# Ensures permissions on UsersTenant are applied here
|
|
@@ -194,7 +194,7 @@ module Strongbolt
|
|
|
194
194
|
|
|
195
195
|
validates :#{singular_association_name}, :presence => true
|
|
196
196
|
RUBY
|
|
197
|
-
Strongbolt.const_set
|
|
197
|
+
Strongbolt.const_set users_tenants_subclass_name, users_tenant_subclass
|
|
198
198
|
end
|
|
199
199
|
end #/create_users_tenant_subclass
|
|
200
200
|
|
|
@@ -208,7 +208,7 @@ module Strongbolt
|
|
|
208
208
|
# Setup the association
|
|
209
209
|
# The first one should never be there before
|
|
210
210
|
user_class.has_many :"users_#{plural_association_name}",
|
|
211
|
-
:class_name => "Strongbolt
|
|
211
|
+
:class_name => "Strongbolt::#{users_tenants_subclass_name}",
|
|
212
212
|
:inverse_of => :user,
|
|
213
213
|
:dependent => :delete_all,
|
|
214
214
|
:foreign_key => :user_id
|
|
@@ -241,6 +241,18 @@ module Strongbolt
|
|
|
241
241
|
end
|
|
242
242
|
end #/setup_association_on_user
|
|
243
243
|
|
|
244
|
+
#
|
|
245
|
+
# returns the name of the subclass of Strongbolt::UsersTenant, containing
|
|
246
|
+
# the associations between users and the tenant model this is running in.
|
|
247
|
+
#
|
|
248
|
+
def users_tenants_subclass_name
|
|
249
|
+
# If the name is something like Strongbolt::UserGroup, we have to remove the double colons.
|
|
250
|
+
# This results to 'StrongboltUserGroup'.
|
|
251
|
+
# The module name is left in the name, so that we won't have any collisions
|
|
252
|
+
# with the same class names in different modules.
|
|
253
|
+
"Users#{self.name.gsub('::', '')}"
|
|
254
|
+
end #/users_tenants_subclass_name
|
|
255
|
+
|
|
244
256
|
#
|
|
245
257
|
# Returns the inverse of specified association, using what's given
|
|
246
258
|
# as inverse_of or trying to guess it
|
|
@@ -292,13 +304,13 @@ module Strongbolt
|
|
|
292
304
|
end
|
|
293
305
|
|
|
294
306
|
end
|
|
295
|
-
|
|
307
|
+
|
|
296
308
|
module InstanceMethods
|
|
297
309
|
end
|
|
298
|
-
|
|
310
|
+
|
|
299
311
|
def self.included(receiver)
|
|
300
312
|
receiver.extend ClassMethods
|
|
301
313
|
receiver.send :include, InstanceMethods
|
|
302
314
|
end
|
|
303
315
|
end
|
|
304
|
-
end
|
|
316
|
+
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module Strongbolt
|
|
2
2
|
module UserAbilities
|
|
3
3
|
module ClassMethods
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
end
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
module InstanceMethods
|
|
8
8
|
#----------------------------------------------------------#
|
|
9
9
|
# #
|
|
@@ -33,16 +33,16 @@ module Strongbolt
|
|
|
33
33
|
#
|
|
34
34
|
def can? action, instance, attrs = :any, all_instance = false
|
|
35
35
|
without_grant do
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
# Get the actual instance if we were given AR
|
|
38
38
|
instance = instance.try(:first) if instance.is_a?(ActiveRecord::Relation)
|
|
39
39
|
return false if instance.nil?
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
# We require this to be an *existing* user, that the action and attribute be symbols
|
|
42
42
|
# and that the instance is a class or a String
|
|
43
|
-
raise ArgumentError, "Action must be a symbol and instance must be Class, String, Symbol or AR" unless self.id.present? && action.is_a?(Symbol) &&
|
|
43
|
+
raise ArgumentError, "Action must be a symbol and instance must be Class, String, Symbol or AR" unless self.id.present? && action.is_a?(Symbol) &&
|
|
44
44
|
(instance.is_a?(ActiveRecord::Base) || instance.is_a?(Class) || instance.is_a?(String)) && attrs.is_a?(Symbol)
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
# Pre-populate all the capabilities into a results cache for quick lookup. Permissions for all "non-owned" objects are
|
|
47
47
|
# immediately available; additional lookups are required for owned objects (e.g. User, CheckoutBag, etc.).
|
|
48
48
|
# The results cache key is formatted as "action model attribute" (attribute can be any, all or an actual attribute)
|
|
@@ -61,11 +61,11 @@ module Strongbolt
|
|
|
61
61
|
# if we don't need it, so just defer until we determine there's an actual need
|
|
62
62
|
model_name = instance
|
|
63
63
|
end
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
# Look up the various possible valid entries in the cache that would allow us to see this
|
|
66
66
|
return capability_in_cache?(action, instance, model_name, attrs, all_instance)
|
|
67
67
|
|
|
68
|
-
end #end w/o grant
|
|
68
|
+
end #end w/o grant
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
#
|
|
@@ -93,14 +93,14 @@ module Strongbolt
|
|
|
93
93
|
#
|
|
94
94
|
def populate_capabilities_cache
|
|
95
95
|
beginning = Time.now
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
@results_cache ||= {}
|
|
98
98
|
@model_ancestor_cache ||= {}
|
|
99
99
|
|
|
100
100
|
# User can find itself by default
|
|
101
101
|
@results_cache["findUserany-any"] = true
|
|
102
102
|
@results_cache["findUserany-#{id}"] = true
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
#
|
|
105
105
|
# Store every capability fetched
|
|
106
106
|
#
|
|
@@ -108,7 +108,7 @@ module Strongbolt
|
|
|
108
108
|
|
|
109
109
|
k = "#{capability.action}#{capability.model}"
|
|
110
110
|
attr_k = capability.attr || 'all'
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
@results_cache["#{k}#{attr_k}-any"] = true
|
|
113
113
|
@results_cache["#{k}any-any"] = true
|
|
114
114
|
|
|
@@ -137,7 +137,7 @@ module Strongbolt
|
|
|
137
137
|
end # End each capability
|
|
138
138
|
|
|
139
139
|
Strongbolt.logger.info "Populated capabilities in #{(Time.now - beginning)*1000}ms"
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
@results_cache
|
|
142
142
|
end # End Populate capabilities Cache
|
|
143
143
|
|
|
@@ -150,10 +150,10 @@ module Strongbolt
|
|
|
150
150
|
# Checks if the user can perform 'action' on 'instance' #
|
|
151
151
|
# #
|
|
152
152
|
#----------------------------------------------------------#
|
|
153
|
-
|
|
153
|
+
|
|
154
154
|
def capability_in_cache?(action, instance, model_name, attrs = :any, all_instance = false)
|
|
155
155
|
action_model = "#{action}#{model_name}"
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
Strongbolt.logger.warn "User has no results cache" if @results_cache.empty?
|
|
158
158
|
Strongbolt.logger.debug { "Authorizing user to perform #{action} on #{instance.inspect}" }
|
|
159
159
|
|
|
@@ -162,13 +162,13 @@ module Strongbolt
|
|
|
162
162
|
# First, check if we have a hash/cache hit for User being able to do this action to every instance of the model/class
|
|
163
163
|
return true if @results_cache["#{action_model}all-all"] #Access to all attributes on ENTIRE class?
|
|
164
164
|
return true if @results_cache["#{action_model}#{attrs}-all"] #Access to this specific attribute on ENTIRE class?
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
# If we're checking on a specific instance of the class, not the general model,
|
|
167
167
|
# append the id to the key
|
|
168
168
|
id = instance.try(:id)
|
|
169
169
|
return true if @results_cache["#{action_model}all-#{id}"] # Access to all this instance's attributes?
|
|
170
170
|
return true if @results_cache["#{action_model}#{attrs}-#{id}"] #Access to this instance's attribute?
|
|
171
|
-
|
|
171
|
+
|
|
172
172
|
# Checking ownership and tenant access
|
|
173
173
|
# Block access for non tenanted instance
|
|
174
174
|
valid_tenants = has_access_to_tenants?(instance)
|
|
@@ -209,14 +209,14 @@ module Strongbolt
|
|
|
209
209
|
return true if @results_cache["#{action_model}#{attrs}-any"] && ! all_instance #Access to this specific attribute on at least once instance?
|
|
210
210
|
end
|
|
211
211
|
#logger.info "Cache miss for checking access to #{key}"
|
|
212
|
-
|
|
212
|
+
|
|
213
213
|
return false
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
#
|
|
217
217
|
# Checks if the instance given fulfills tenant management rules
|
|
218
218
|
#
|
|
219
|
-
def has_access_to_tenants? instance, tenants = nil
|
|
219
|
+
def has_access_to_tenants? instance, tenants = nil
|
|
220
220
|
# If no tenants list given, we take all
|
|
221
221
|
tenants ||= Strongbolt.tenants
|
|
222
222
|
# Populate the cache if needed
|
|
@@ -256,7 +256,7 @@ module Strongbolt
|
|
|
256
256
|
return if @tenants_cache.present?
|
|
257
257
|
|
|
258
258
|
Strongbolt.logger.debug "Populating tenants cache for user #{self.id}"
|
|
259
|
-
|
|
259
|
+
|
|
260
260
|
@tenants_cache = {}
|
|
261
261
|
# Go over each tenants
|
|
262
262
|
Strongbolt.tenants.each do |tenant|
|
|
@@ -267,7 +267,7 @@ module Strongbolt
|
|
|
267
267
|
|
|
268
268
|
|
|
269
269
|
end # End InstanceMethods
|
|
270
|
-
|
|
270
|
+
|
|
271
271
|
def self.included(receiver)
|
|
272
272
|
receiver.extend ClassMethods
|
|
273
273
|
receiver.send :include, InstanceMethods
|
|
@@ -279,7 +279,7 @@ module Strongbolt
|
|
|
279
279
|
:inverse_of => :user,
|
|
280
280
|
:foreign_key => :user_id
|
|
281
281
|
has_many :user_groups, :through => :user_groups_users
|
|
282
|
-
|
|
282
|
+
|
|
283
283
|
has_many :roles, through: :user_groups
|
|
284
284
|
end
|
|
285
285
|
|
|
@@ -289,4 +289,4 @@ module Strongbolt
|
|
|
289
289
|
end
|
|
290
290
|
end
|
|
291
291
|
end
|
|
292
|
-
end
|
|
292
|
+
end
|
|
@@ -6,7 +6,7 @@ module Strongbolt
|
|
|
6
6
|
:dependent => :restrict_with_exception,
|
|
7
7
|
:inverse_of => :user_group
|
|
8
8
|
has_many :users, :through => :user_groups_users
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
has_many :roles_user_groups,
|
|
11
11
|
:class_name => "Strongbolt::RolesUserGroup",
|
|
12
12
|
:dependent => :delete_all,
|
|
@@ -20,5 +20,3 @@ module Strongbolt
|
|
|
20
20
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
|
-
|
|
24
|
-
UserGroup = Strongbolt::UserGroup unless defined? UserGroup
|
|
@@ -5,7 +5,7 @@ module Strongbolt
|
|
|
5
5
|
belongs_to :user_group,
|
|
6
6
|
:class_name => "Strongbolt::UserGroup",
|
|
7
7
|
:inverse_of => :user_groups_users
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
belongs_to :user,
|
|
10
10
|
:class_name => Configuration.user_class,
|
|
11
11
|
:foreign_key => :user_id,
|
|
@@ -13,4 +13,4 @@ module Strongbolt
|
|
|
13
13
|
|
|
14
14
|
validates_presence_of :user_group, :user
|
|
15
15
|
end
|
|
16
|
-
end
|
|
16
|
+
end
|
data/lib/strongbolt/version.rb
CHANGED
|
@@ -7,23 +7,23 @@ namespace :strongbolt do
|
|
|
7
7
|
#
|
|
8
8
|
# Creates capabilities for all models/actions
|
|
9
9
|
#
|
|
10
|
-
Capability.models.each do |model|
|
|
11
|
-
Capability::Actions.each do |action|
|
|
12
|
-
Capability.where(model: model, action: action,
|
|
10
|
+
Strongbolt::Capability.models.each do |model|
|
|
11
|
+
Strongbolt::Capability::Actions.each do |action|
|
|
12
|
+
Strongbolt::Capability.where(model: model, action: action,
|
|
13
13
|
require_tenant_access: false).first_or_create
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# The role
|
|
18
|
-
role = Role.create! name: "FULL ACCESS (TEMPORARY)"
|
|
19
|
-
role.capabilities = Capability.all
|
|
18
|
+
role = Strongbolt::Role.create! name: "FULL ACCESS (TEMPORARY)"
|
|
19
|
+
role.capabilities = Strongbolt::Capability.all
|
|
20
20
|
|
|
21
21
|
# The user group
|
|
22
|
-
ug = UserGroup.create! name: "FULL ACCESS USERS (TEMPORARY)"
|
|
22
|
+
ug = Strongbolt::UserGroup.create! name: "FULL ACCESS USERS (TEMPORARY)"
|
|
23
23
|
ug.roles << role
|
|
24
24
|
|
|
25
25
|
# Assign to all users
|
|
26
26
|
User.all.each { |user| user.user_groups << ug }
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
|
-
end
|
|
29
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
2
|
|
|
3
3
|
describe Strongbolt::Configuration do
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
|
|
6
6
|
#
|
|
7
7
|
# User class
|
|
8
8
|
#
|
|
9
9
|
describe "user class" do
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
it "should default to User" do
|
|
12
12
|
expect(Strongbolt::Configuration.user_class).to eq "User"
|
|
13
13
|
end
|
|
@@ -29,7 +29,7 @@ describe Strongbolt::Configuration do
|
|
|
29
29
|
# Setting up tenants
|
|
30
30
|
#
|
|
31
31
|
describe 'tenants=' do
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
before do
|
|
34
34
|
define_model "Model" do
|
|
35
35
|
self.table_name = "models"
|
|
@@ -59,11 +59,11 @@ describe Strongbolt::Configuration do
|
|
|
59
59
|
Strongbolt::Configuration.models = "OtherModel", "Model"
|
|
60
60
|
end
|
|
61
61
|
after do
|
|
62
|
-
Capability.models = nil
|
|
62
|
+
Strongbolt::Capability.models = nil
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
it "should set Capability::Models" do
|
|
66
|
-
expect(Capability.models).to eq ["Model", "OtherModel", "Strongbolt::Capability", "Strongbolt::Role", "Strongbolt::UserGroup", "Strongbolt::UsersTenant"]
|
|
66
|
+
expect(Strongbolt::Capability.models).to eq ["Model", "OtherModel", "Strongbolt::Capability", "Strongbolt::Role", "Strongbolt::UserGroup", "Strongbolt::UsersTenant"]
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
context "when adding other models" do
|
|
@@ -72,7 +72,7 @@ describe Strongbolt::Configuration do
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
it "should merge with current models" do
|
|
75
|
-
expect(Capability.models).to eq ["LastModel", "Model", "OtherModel", "Strongbolt::Capability", "Strongbolt::Role", "Strongbolt::UserGroup", "Strongbolt::UsersTenant"]
|
|
75
|
+
expect(Strongbolt::Capability.models).to eq ["LastModel", "Model", "OtherModel", "Strongbolt::Capability", "Strongbolt::Role", "Strongbolt::UserGroup", "Strongbolt::UsersTenant"]
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
@@ -82,7 +82,7 @@ describe Strongbolt::Configuration do
|
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
it "should merge with current models" do
|
|
85
|
-
expect(Capability.models).to eq ["BottomModel", "Model", "OtherModel", "Strongbolt::Capability", "Strongbolt::Role", "Strongbolt::UserGroup", "Strongbolt::UsersTenant"]
|
|
85
|
+
expect(Strongbolt::Capability.models).to eq ["BottomModel", "Model", "OtherModel", "Strongbolt::Capability", "Strongbolt::Role", "Strongbolt::UserGroup", "Strongbolt::UsersTenant"]
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
end #/models=
|
|
@@ -93,7 +93,7 @@ describe Strongbolt::Configuration do
|
|
|
93
93
|
# Setting default permissions
|
|
94
94
|
#
|
|
95
95
|
describe "default_capabilities=" do
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
before do
|
|
98
98
|
Strongbolt::Configuration.default_capabilities = [
|
|
99
99
|
{:model => "User", :actions => :all},
|
|
@@ -110,10 +110,10 @@ describe Strongbolt::Configuration do
|
|
|
110
110
|
|
|
111
111
|
it "should return Capability" do
|
|
112
112
|
Strongbolt::Configuration.default_capabilities.each do |c|
|
|
113
|
-
expect(c).to be_a Capability
|
|
113
|
+
expect(c).to be_a Strongbolt::Capability
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
end
|
|
119
|
+
end
|
data/strongbolt.gemspec
CHANGED
|
@@ -25,8 +25,8 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.add_development_dependency "rails", "~> 4.1.0"
|
|
26
26
|
spec.add_development_dependency "bundler", "> 1.7.0"
|
|
27
27
|
spec.add_development_dependency "rake"
|
|
28
|
-
spec.add_development_dependency 'sqlite3'
|
|
29
|
-
spec.add_development_dependency 'shoulda-matchers'
|
|
28
|
+
spec.add_development_dependency 'sqlite3'
|
|
29
|
+
spec.add_development_dependency 'shoulda-matchers', '~> 2.7.0'
|
|
30
30
|
spec.add_development_dependency 'fuubar'
|
|
31
31
|
spec.add_development_dependency 'rspec-rails', '~> 3.1.0'
|
|
32
32
|
spec.add_development_dependency 'fabrication'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: strongbolt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Thomas Césaré-Herriau
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2016-
|
|
12
|
+
date: 2016-05-16 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: awesome_nested_set
|
|
@@ -99,30 +99,30 @@ dependencies:
|
|
|
99
99
|
name: sqlite3
|
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
|
101
101
|
requirements:
|
|
102
|
-
- -
|
|
102
|
+
- - ">="
|
|
103
103
|
- !ruby/object:Gem::Version
|
|
104
|
-
version:
|
|
104
|
+
version: '0'
|
|
105
105
|
type: :development
|
|
106
106
|
prerelease: false
|
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
|
108
108
|
requirements:
|
|
109
|
-
- -
|
|
109
|
+
- - ">="
|
|
110
110
|
- !ruby/object:Gem::Version
|
|
111
|
-
version:
|
|
111
|
+
version: '0'
|
|
112
112
|
- !ruby/object:Gem::Dependency
|
|
113
113
|
name: shoulda-matchers
|
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
|
115
115
|
requirements:
|
|
116
|
-
- - "
|
|
116
|
+
- - "~>"
|
|
117
117
|
- !ruby/object:Gem::Version
|
|
118
|
-
version:
|
|
118
|
+
version: 2.7.0
|
|
119
119
|
type: :development
|
|
120
120
|
prerelease: false
|
|
121
121
|
version_requirements: !ruby/object:Gem::Requirement
|
|
122
122
|
requirements:
|
|
123
|
-
- - "
|
|
123
|
+
- - "~>"
|
|
124
124
|
- !ruby/object:Gem::Version
|
|
125
|
-
version:
|
|
125
|
+
version: 2.7.0
|
|
126
126
|
- !ruby/object:Gem::Dependency
|
|
127
127
|
name: fuubar
|
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|