erp_tech_svcs 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/app/models/group.rb +17 -3
- data/app/models/user.rb +40 -10
- data/lib/erp_tech_svcs/extensions/active_record/has_capability_accessors.rb +10 -4
- data/lib/erp_tech_svcs/extensions/active_record/protected_with_capabilities.rb +53 -28
- data/lib/erp_tech_svcs/utils/compass_access_negotiator.rb +5 -3
- data/lib/erp_tech_svcs/version.rb +1 -1
- data/lib/tasks/erp_tech_svcs_tasks.rake +2 -2
- data/spec/lib/erp_tech_svcs/extensions/active_record/has_roles_spec.rb +8 -3
- data/spec/spec_helper.rb +3 -1
- metadata +2 -8
data/app/models/group.rb
CHANGED
@@ -140,15 +140,29 @@ class Group < ActiveRecord::Base
|
|
140
140
|
end
|
141
141
|
|
142
142
|
def role_class_capabilities
|
143
|
-
|
143
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
144
|
+
Capability.joins(:capability_type).joins(:capability_accessors).
|
145
|
+
where(:capability_accessors => { :capability_accessor_record_type => "SecurityRole" }).
|
146
|
+
where("capability_accessor_record_id IN (#{roles.select('security_roles.id').to_sql})").
|
147
|
+
where(:scope_type_id => scope_type.id)
|
144
148
|
end
|
145
149
|
|
146
150
|
def all_class_capabilities
|
147
|
-
|
151
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
152
|
+
Capability.joins(:capability_type).joins(:capability_accessors).
|
153
|
+
where("(capability_accessors.capability_accessor_record_type = 'Group' AND
|
154
|
+
capability_accessor_record_id = (#{self.id})) OR
|
155
|
+
(capability_accessors.capability_accessor_record_type = 'SecurityRole' AND
|
156
|
+
capability_accessor_record_id IN (#{roles.select('security_roles.id').to_sql}))").
|
157
|
+
where(:scope_type_id => scope_type.id)
|
158
|
+
end
|
159
|
+
|
160
|
+
def all_uniq_class_capabilities
|
161
|
+
all_class_capabilities.all.uniq
|
148
162
|
end
|
149
163
|
|
150
164
|
def class_capabilities_to_hash
|
151
|
-
|
165
|
+
all_uniq_class_capabilities.map {|capability|
|
152
166
|
{ :capability_type_iid => capability.capability_type.internal_identifier,
|
153
167
|
:capability_resource_type => capability.capability_resource_type
|
154
168
|
}
|
data/app/models/user.rb
CHANGED
@@ -46,7 +46,7 @@ class User < ActiveRecord::Base
|
|
46
46
|
passed_roles.flatten!
|
47
47
|
passed_roles.each do |role|
|
48
48
|
role_iid = role.is_a?(SecurityRole) ? role.internal_identifier : role.to_s
|
49
|
-
|
49
|
+
all_uniq_roles.each do |this_role|
|
50
50
|
result = true if (this_role.internal_identifier == role_iid)
|
51
51
|
break if result
|
52
52
|
end
|
@@ -103,40 +103,70 @@ class User < ActiveRecord::Base
|
|
103
103
|
|
104
104
|
# roles assigned to the groups this user belongs to
|
105
105
|
def group_roles
|
106
|
-
|
106
|
+
SecurityRole.joins(:parties).
|
107
|
+
where(:parties => {:business_party_type => 'Group'}).
|
108
|
+
where("parties.business_party_id IN (#{groups.select('groups.id').to_sql})")
|
107
109
|
end
|
108
110
|
|
109
111
|
# composite roles for this user
|
110
112
|
def all_roles
|
111
|
-
(
|
113
|
+
SecurityRole.joins(:parties).joins("LEFT JOIN users ON parties.id=users.party_id").
|
114
|
+
where("(parties.business_party_type='Group' AND
|
115
|
+
parties.business_party_id IN (#{groups.select('groups.id').to_sql})) OR
|
116
|
+
(users.id=#{self.id})")
|
117
|
+
end
|
118
|
+
|
119
|
+
def all_uniq_roles
|
120
|
+
all_roles.all.uniq
|
112
121
|
end
|
113
122
|
|
114
123
|
def group_capabilities
|
115
|
-
|
124
|
+
Capability.joins(:capability_type).joins(:capability_accessors).
|
125
|
+
where(:capability_accessors => { :capability_accessor_record_type => "Group" }).
|
126
|
+
where("capability_accessor_record_id IN (#{groups.select('groups.id').to_sql})")
|
116
127
|
end
|
117
128
|
|
118
129
|
def role_capabilities
|
119
|
-
|
130
|
+
Capability.joins(:capability_type).joins(:capability_accessors).
|
131
|
+
where(:capability_accessors => { :capability_accessor_record_type => "SecurityRole" }).
|
132
|
+
where("capability_accessor_record_id IN (#{all_roles.select('security_roles.id').to_sql})")
|
120
133
|
end
|
121
134
|
|
122
135
|
def all_capabilities
|
123
|
-
(
|
136
|
+
Capability.joins(:capability_type).joins(:capability_accessors).
|
137
|
+
where("(capability_accessors.capability_accessor_record_type = 'Group' AND
|
138
|
+
capability_accessor_record_id IN (#{groups.select('groups.id').to_sql})) OR
|
139
|
+
(capability_accessors.capability_accessor_record_type = 'SecurityRole' AND
|
140
|
+
capability_accessor_record_id IN (#{all_roles.select('security_roles.id').to_sql})) OR
|
141
|
+
(capability_accessors.capability_accessor_record_type = 'User' AND
|
142
|
+
capability_accessor_record_id = #{self.id})")
|
143
|
+
end
|
144
|
+
|
145
|
+
def all_uniq_capabilities
|
146
|
+
all_capabilities.all.uniq
|
124
147
|
end
|
125
148
|
|
126
149
|
def group_class_capabilities
|
127
|
-
|
150
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
151
|
+
group_capabilities.where(:scope_type_id => scope_type.id)
|
128
152
|
end
|
129
153
|
|
130
154
|
def role_class_capabilities
|
131
|
-
|
155
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
156
|
+
role_capabilities.where(:scope_type_id => scope_type.id)
|
132
157
|
end
|
133
158
|
|
134
159
|
def all_class_capabilities
|
135
|
-
|
160
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
161
|
+
all_capabilities.where(:scope_type_id => scope_type.id)
|
162
|
+
end
|
163
|
+
|
164
|
+
def all_uniq_class_capabilities
|
165
|
+
all_class_capabilities.all.uniq
|
136
166
|
end
|
137
167
|
|
138
168
|
def class_capabilities_to_hash
|
139
|
-
|
169
|
+
all_uniq_class_capabilities.map {|capability|
|
140
170
|
{ :capability_type_iid => capability.capability_type.internal_identifier,
|
141
171
|
:capability_resource_type => capability.capability_resource_type
|
142
172
|
}
|
@@ -77,7 +77,8 @@ module ErpTechSvcs
|
|
77
77
|
|
78
78
|
# pass in (capability_type_iid, klass) or (capability) object
|
79
79
|
def add_capability(*capability)
|
80
|
-
|
80
|
+
capability_type_iid = capability.first.is_a?(Symbol) ? capability.first.to_s : capability.first
|
81
|
+
capability = capability_type_iid.is_a?(String) ? get_or_create_capability(capability_type_iid, capability.second) : capability.first
|
81
82
|
ca = CapabilityAccessor.find_or_create_by_capability_accessor_record_type_and_capability_accessor_record_id_and_capability_id(get_superclass, self.id, capability.id)
|
82
83
|
self.reload
|
83
84
|
ca
|
@@ -89,8 +90,12 @@ module ErpTechSvcs
|
|
89
90
|
|
90
91
|
def get_or_create_capability(capability_type_iid, klass)
|
91
92
|
capability_type = convert_capability_type(capability_type_iid)
|
92
|
-
|
93
|
-
|
93
|
+
if klass.is_a?(String)
|
94
|
+
scope_type = ScopeType.find_by_internal_identifier('class')
|
95
|
+
Capability.find_or_create_by_capability_resource_type_and_capability_type_id_and_scope_type_id(klass, capability_type.id, scope_type.id)
|
96
|
+
else
|
97
|
+
klass.add_capability(capability_type_iid) # create instance capability
|
98
|
+
end
|
94
99
|
end
|
95
100
|
|
96
101
|
def get_capability(capability_type_iid, klass)
|
@@ -101,7 +106,8 @@ module ErpTechSvcs
|
|
101
106
|
|
102
107
|
# pass in (capability_type_iid, klass) or (capability) object
|
103
108
|
def remove_capability(*capability)
|
104
|
-
|
109
|
+
capability_type_iid = capability.first.is_a?(Symbol) ? capability.first.to_s : capability.first
|
110
|
+
capability = capability_type_iid.is_a?(String) ? get_or_create_capability(capability_type_iid, capability.second) : capability.first
|
105
111
|
ca = capability_accessors.where(:capability_accessor_record_type => get_superclass, :capability_accessor_record_id => self.id, :capability_id => capability.id).first
|
106
112
|
ca.destroy unless ca.nil?
|
107
113
|
self.reload
|
@@ -9,15 +9,24 @@ module ErpTechSvcs
|
|
9
9
|
|
10
10
|
module ClassMethods
|
11
11
|
|
12
|
-
def protected_with_capabilities
|
12
|
+
def protected_with_capabilities(options = {})
|
13
13
|
extend ProtectedByCapabilities::SingletonMethods
|
14
14
|
include ProtectedByCapabilities::InstanceMethods
|
15
|
-
|
16
|
-
has_many :capabilities, :as => :capability_resource
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
#
|
16
|
+
has_many :capabilities, :as => :capability_resource
|
17
|
+
|
18
|
+
# protect all instance of this class by default
|
19
|
+
class_attribute :protect_all_instances
|
20
|
+
self.protect_all_instances = (options[:protect_all_instances].nil? ? false : options[:protect_all_instances])
|
21
|
+
|
22
|
+
# Get records filtered via query scope capabilities
|
23
|
+
# By default Compass AE treats query scopes as restrictions
|
24
|
+
# A user will see all records unless the user has a capability accessor with a query scope
|
25
|
+
# If you set :protect_all_instances => true it is honored via with_user_security & with_instance_security but NOT with_query_security
|
26
|
+
# arguments: user, capability_type_iids
|
27
|
+
# capability_type_iids is optional and can be a single string or an array of strings
|
28
|
+
# Example: which files can this user download? FileAsset.with_query_security(user, 'download').all
|
29
|
+
# Example: which website sections can this user either view or edit? WebsiteSection.with_query_security(user, ['view','edit']).all
|
21
30
|
scope :with_query_security, lambda{|*args|
|
22
31
|
raise ArgumentError if args.empty? || args.size > 2
|
23
32
|
user = args.first
|
@@ -25,11 +34,11 @@ module ErpTechSvcs
|
|
25
34
|
capability_type_iids = [capability_type_iids] if capability_type_iids.is_a?(String)
|
26
35
|
|
27
36
|
scope_type = ScopeType.find_by_internal_identifier('query')
|
28
|
-
granted_capabilities = user.all_capabilities.
|
37
|
+
granted_capabilities = user.all_capabilities.where(:scope_type_id => scope_type.id).where(:capability_resource_type => self.name)
|
29
38
|
|
30
39
|
unless capability_type_iids.empty?
|
31
40
|
capability_type_ids = capability_type_iids.collect{|type| convert_capability_type(type).id }
|
32
|
-
granted_capabilities = granted_capabilities.
|
41
|
+
granted_capabilities = granted_capabilities.where("capability_type_id IN (?)", capability_type_ids.join(','))
|
33
42
|
end
|
34
43
|
|
35
44
|
query = nil
|
@@ -39,34 +48,45 @@ module ErpTechSvcs
|
|
39
48
|
query
|
40
49
|
}
|
41
50
|
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
group(columns.collect{|c| "#{self.table_name}.#{c.name}" })
|
46
|
-
query = (denied_capabilities.empty? ? query.where("c.id IS NULL OR c.id = c.id") : query.where("c.id IS NULL OR c.id NOT IN (?)", denied_capabilities.collect{|c| c.id }))
|
47
|
-
query
|
48
|
-
}
|
49
|
-
|
50
|
-
# get records for this model that the given user has access to
|
51
|
+
# Get records for this model permitted via instance capabilities
|
52
|
+
# If :protect_all_instances => true return only instances user has explicitly been granted access to
|
53
|
+
# If :protect_all_instances => false return instances without capabilities or that user is granted access to (default)
|
51
54
|
# arguments: user, capability_type_iids
|
52
55
|
# capability_type_iids is optional and can be a single string or an array of strings
|
53
|
-
# Example: which files can this user download? FileAsset.
|
54
|
-
# Example: which website sections can this user either view or edit? WebsiteSection.
|
55
|
-
scope :
|
56
|
+
# Example: which files can this user download? FileAsset.with_instance_security(user, 'download').all
|
57
|
+
# Example: which website sections can this user either view or edit? WebsiteSection.with_instance_security(user, ['view','edit']).all
|
58
|
+
scope :with_instance_security, lambda{|*args|
|
56
59
|
raise ArgumentError if args.empty? || args.size > 2
|
57
60
|
user = args.first
|
58
61
|
capability_type_iids = args.second || []
|
59
62
|
capability_type_iids = [capability_type_iids] if capability_type_iids.is_a?(String)
|
60
63
|
|
61
64
|
scope_type = ScopeType.find_by_internal_identifier('instance')
|
62
|
-
granted_capabilities = user.all_capabilities.
|
65
|
+
granted_capabilities = user.all_capabilities.where(:scope_type_id => scope_type.id).where(:capability_resource_type => self.name)
|
63
66
|
|
64
67
|
unless capability_type_iids.empty?
|
65
68
|
capability_type_ids = capability_type_iids.collect{|type| convert_capability_type(type).id }
|
66
|
-
granted_capabilities = granted_capabilities.
|
69
|
+
granted_capabilities = granted_capabilities.where("capability_type_id IN (#{capability_type_ids.join(',')})")
|
67
70
|
end
|
68
|
-
|
69
|
-
|
71
|
+
|
72
|
+
denied_capabilities = instance_capabilities.select('capabilities.id').where("capabilities.id NOT IN (#{granted_capabilities.select('capabilities.id').to_sql})")
|
73
|
+
deny_count = denied_capabilities.count
|
74
|
+
|
75
|
+
join_type = (self.protect_all_instances ? 'JOIN' : 'LEFT JOIN')
|
76
|
+
query = joins("#{join_type} capabilities AS c ON c.capability_resource_id = #{self.table_name}.id AND c.capability_resource_type = '#{self.name}'").
|
77
|
+
group(columns.collect{|c| "#{self.table_name}.#{c.name}" })
|
78
|
+
query = (deny_count == 0 ? query.where("c.id IS NULL OR c.id = c.id") : query.where("c.id IS NULL OR c.id NOT IN (#{denied_capabilities.to_sql})"))
|
79
|
+
query
|
80
|
+
}
|
81
|
+
|
82
|
+
# Get records for this model that the given user has access to
|
83
|
+
# arguments: user, capability_type_iids
|
84
|
+
# capability_type_iids is optional and can be a single string or an array of strings
|
85
|
+
# Example: which files can this user download? FileAsset.with_user_security(user, 'download').all
|
86
|
+
# Example: which website sections can this user either view or edit? WebsiteSection.with_user_security(user, ['view','edit']).all
|
87
|
+
scope :with_user_security, lambda{|*args|
|
88
|
+
raise ArgumentError if args.empty? || args.size > 2
|
89
|
+
with_instance_security(*args).with_query_security(*args)
|
70
90
|
}
|
71
91
|
end
|
72
92
|
end
|
@@ -102,9 +122,9 @@ module ErpTechSvcs
|
|
102
122
|
capabilities.where(:scope_type_id => scope_type.id)
|
103
123
|
end
|
104
124
|
|
105
|
-
#
|
125
|
+
# return unique roles on capabilities for this model
|
106
126
|
def capability_roles
|
107
|
-
|
127
|
+
SecurityRole.joins(:capability_accessors => :capability).where(:capability_accessors => {:capabilities => {:capability_resource_type => get_superclass(self.name) }}).all.uniq
|
108
128
|
end
|
109
129
|
|
110
130
|
# add a class level capability (capability_resource_id will be NULL)
|
@@ -147,6 +167,11 @@ module ErpTechSvcs
|
|
147
167
|
|
148
168
|
module InstanceMethods
|
149
169
|
|
170
|
+
# convenience method to access class method
|
171
|
+
def protect_all_instances
|
172
|
+
self.class.protect_all_instances
|
173
|
+
end
|
174
|
+
|
150
175
|
def add_capability(capability_type_iid)
|
151
176
|
capability_type = convert_capability_type(capability_type_iid)
|
152
177
|
scope_type = ScopeType.find_by_internal_identifier('instance')
|
@@ -165,11 +190,11 @@ module ErpTechSvcs
|
|
165
190
|
end
|
166
191
|
|
167
192
|
def protected_with_capability?(capability_type_iid)
|
168
|
-
!get_capability(capability_type_iid).nil?
|
193
|
+
!get_capability(capability_type_iid).nil? or protect_all_instances
|
169
194
|
end
|
170
195
|
|
171
196
|
def allow_access?(user, capability_type_iid)
|
172
|
-
if !self.protected_with_capability?(capability_type_iid.to_s) or (user and user.has_capability?(capability_type_iid.to_s, self))
|
197
|
+
if (!self.protect_all_instances and !self.protected_with_capability?(capability_type_iid.to_s)) or (user and user.has_capability?(capability_type_iid.to_s, self))
|
173
198
|
return true
|
174
199
|
else
|
175
200
|
return false
|
@@ -13,15 +13,17 @@ module ErpTechSvcs
|
|
13
13
|
where(:capability_resource_type => klass).
|
14
14
|
where(:scope_type_id => scope_type.id).
|
15
15
|
where(:capability_types => {:internal_identifier => capability_type_iid}).first
|
16
|
+
return nil if capability.nil? # capability not found so return nil
|
16
17
|
else
|
17
18
|
scope_type = ScopeType.find_by_internal_identifier('instance')
|
18
19
|
capability = klass.capabilities.joins(:capability_type).
|
19
20
|
where(:scope_type_id => scope_type.id).
|
20
21
|
where(:capability_types => {:internal_identifier => capability_type_iid}).first
|
21
|
-
|
22
|
+
# if capability not found, we see if all instances are protected
|
23
|
+
# if all instance are protected, return false, otherwise true
|
24
|
+
return !klass.protect_all_instances if capability.nil?
|
22
25
|
end
|
23
|
-
|
24
|
-
result.nil? ? false : true
|
26
|
+
all_capabilities.include?(capability)
|
25
27
|
end
|
26
28
|
|
27
29
|
# pass in (capability_type_iid, class name or any class instance, a block of code)
|
@@ -8,8 +8,8 @@ namespace :erp_tech_svcs do
|
|
8
8
|
|
9
9
|
#sync shared
|
10
10
|
puts "Syncing Shared Assets..."
|
11
|
-
file_support.sync(File.join(file_support.root, '/images'), CompassAeInstance.
|
12
|
-
file_support.sync(File.join(file_support.root, '/files'), CompassAeInstance.
|
11
|
+
file_support.sync(File.join(file_support.root, '/images'), CompassAeInstance.find_by_internal_identifier('base'))
|
12
|
+
file_support.sync(File.join(file_support.root, '/files'), CompassAeInstance.find_by_internal_identifier('base'))
|
13
13
|
puts "Complete"
|
14
14
|
|
15
15
|
#sync websites
|
@@ -4,17 +4,20 @@ describe ErpTechSvcs::Extensions::ActiveRecord::HasSecurityRoles do
|
|
4
4
|
before(:all) do
|
5
5
|
@user = FactoryGirl.create(:user)
|
6
6
|
@user_2 = FactoryGirl.create(:user)
|
7
|
-
|
8
|
-
@
|
9
|
-
@
|
7
|
+
|
8
|
+
@admin_role = SecurityRole.find_or_create_by_description_and_internal_identifier(:description => 'Admin', :internal_identifier => 'admin')
|
9
|
+
@employee_role = SecurityRole.find_or_create_by_description_and_internal_identifier(:description => 'Employee', :internal_identifier => 'employee')
|
10
|
+
@manager_role = SecurityRole.find_or_create_by_description_and_internal_identifier(:description => 'Manager', :internal_identifier => 'manager')
|
10
11
|
end
|
11
12
|
|
12
13
|
it "should allow you to add a role" do
|
13
14
|
@user.add_role(@admin_role)
|
15
|
+
@user.remove_all_roles
|
14
16
|
end
|
15
17
|
|
16
18
|
it "should allow you to add multiple roles by Role instance or iid" do
|
17
19
|
@user.add_roles(@admin_role, 'manager')
|
20
|
+
@user.remove_all_roles
|
18
21
|
end
|
19
22
|
|
20
23
|
it "should allow you to add multiple roles by array or arguments" do
|
@@ -35,11 +38,13 @@ describe ErpTechSvcs::Extensions::ActiveRecord::HasSecurityRoles do
|
|
35
38
|
end
|
36
39
|
|
37
40
|
it "should allow you to remove a role" do
|
41
|
+
@user.add_role(@admin_role)
|
38
42
|
@user.remove_role(@admin_role)
|
39
43
|
@user.has_role?(@admin_role).should eq false
|
40
44
|
end
|
41
45
|
|
42
46
|
it "should allow you to remove multiple roles by Role instance or iid" do
|
47
|
+
@user.add_roles(@admin_role, 'manager')
|
43
48
|
@user.remove_roles(@employee_role, 'manager')
|
44
49
|
@user.has_role?(@employee_role).should eq false
|
45
50
|
@user.has_role?('manager').should eq false
|
data/spec/spec_helper.rb
CHANGED
@@ -20,6 +20,8 @@ Spork.prefork do
|
|
20
20
|
require File.expand_path(DUMMY_APP_ROOT + "/config/environment.rb", __FILE__)
|
21
21
|
|
22
22
|
ActiveRecord::Base.configurations = YAML::load(IO.read(DUMMY_APP_ROOT + "/config/database.yml"))
|
23
|
+
`rake db:drop RAILS_ENV=spec`
|
24
|
+
`rake db:create RAILS_ENV=spec`
|
23
25
|
ActiveRecord::Base.establish_connection(ENV["DB"] || "spec")
|
24
26
|
ActiveRecord::Migration.verbose = false
|
25
27
|
|
@@ -43,12 +45,12 @@ Spork.each_run do
|
|
43
45
|
#We have to execute the migrations from dummy app directory
|
44
46
|
Dir.chdir DUMMY_APP_ROOT
|
45
47
|
`rake db:drop RAILS_ENV=spec`
|
48
|
+
`rake db:create RAILS_ENV=spec`
|
46
49
|
Dir.chdir ENGINE_RAILS_ROOT
|
47
50
|
|
48
51
|
#We have to execute the migratiapp:compass_ae:install:data_migrationsons from dummy app directory
|
49
52
|
Dir.chdir DUMMY_APP_ROOT
|
50
53
|
|
51
|
-
|
52
54
|
`rake compass_ae:install:migrations RAILS_ENV=spec`
|
53
55
|
`rake compass_ae:install:data_migrations RAILS_ENV=spec`
|
54
56
|
`rake db:migrate RAILS_ENV=spec`
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erp_tech_svcs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-02-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: erp_base_erp_svcs
|
@@ -305,18 +305,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
305
305
|
- - ! '>='
|
306
306
|
- !ruby/object:Gem::Version
|
307
307
|
version: '0'
|
308
|
-
segments:
|
309
|
-
- 0
|
310
|
-
hash: 697401427352755655
|
311
308
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
312
309
|
none: false
|
313
310
|
requirements:
|
314
311
|
- - ! '>='
|
315
312
|
- !ruby/object:Gem::Version
|
316
313
|
version: '0'
|
317
|
-
segments:
|
318
|
-
- 0
|
319
|
-
hash: 697401427352755655
|
320
314
|
requirements: []
|
321
315
|
rubyforge_project:
|
322
316
|
rubygems_version: 1.8.24
|