platanus 0.0.32 → 0.0.49
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.
- data/Gemfile +0 -2
- data/lib/platanus/activable.rb +2 -0
- data/lib/platanus/canned2.rb +10 -64
- data/lib/platanus/stacked2.rb +169 -81
- data/lib/platanus/version.rb +1 -1
- data/platanus.gemspec +2 -1
- data/spec/canned2_spec.rb +3 -9
- metadata +18 -3
data/Gemfile
CHANGED
data/lib/platanus/activable.rb
CHANGED
@@ -47,6 +47,8 @@ module Platanus
|
|
47
47
|
self.transaction do
|
48
48
|
run_callbacks :remove do
|
49
49
|
|
50
|
+
# TODO: disable update callbacks and validations!
|
51
|
+
|
50
52
|
# Retrieve dependant properties and remove them.
|
51
53
|
self.class.reflect_on_all_associations.select do |assoc|
|
52
54
|
if assoc.options[:dependent] == :destroy
|
data/lib/platanus/canned2.rb
CHANGED
@@ -94,78 +94,19 @@ module Platanus
|
|
94
94
|
# TODO: example
|
95
95
|
class Profile
|
96
96
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
rule_ctx.instance_eval(&rule)
|
101
|
-
rule_ctx.passed?
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def is_forbidden()
|
106
|
-
# TODO: local forbids
|
107
|
-
end
|
108
|
-
|
109
|
-
def is_allowed()
|
110
|
-
end
|
111
|
-
|
112
|
-
def
|
113
|
-
|
114
|
-
def allowance_for(_ctx, _action, _tests)
|
115
|
-
|
116
|
-
# all shared rules must pass
|
117
|
-
return :not_applicable unless @shared.all? do |rule|
|
118
|
-
rule_ctx = RuleContext.new _ctx, _tests, @def_matcher, @def_resource
|
119
|
-
rule_ctx.instance_eval(&rule)
|
120
|
-
rule_ctx.passed?
|
121
|
-
end
|
122
|
-
|
123
|
-
# TODO: local forbids
|
124
|
-
|
125
|
-
# process base profile
|
126
|
-
unless @base.nil?
|
127
|
-
result = @base.process _ctx, _action, _tests
|
128
|
-
return result if result == :not_applicable or result == :forbidden
|
129
|
-
matches << resu
|
130
|
-
end
|
131
|
-
|
132
|
-
# process subprofiles
|
133
|
-
if @isolated.each do |profile|
|
134
|
-
result = profile.process _ctx, _action, _tests
|
135
|
-
return :forbidden if result == :forbidden
|
136
|
-
result = :allowed if result == :allowed
|
137
|
-
end
|
138
|
-
|
139
|
-
# see if any of the registered rules pass
|
140
|
-
return :allowed if @rules[_action].any? do |rule|
|
141
|
-
rule_ctx = RuleContext.new _ctx, _tests, @def_matcher, @def_resource
|
142
|
-
rule_ctx.instance_eval(&rule)
|
143
|
-
rule_ctx.passed?
|
144
|
-
end
|
145
|
-
|
146
|
-
return :not_allowed
|
147
|
-
end
|
97
|
+
attr_reader :rules
|
98
|
+
attr_reader :def_matcher
|
99
|
+
attr_reader :def_resource
|
148
100
|
|
149
101
|
# The initializer takes another profile as rules base.
|
150
|
-
def initialize(_def_matcher, _def_resource
|
151
|
-
@base = _base
|
102
|
+
def initialize(_base, _def_matcher, _def_resource)
|
152
103
|
@rules = Hash.new { |h, k| h[k] = [] }
|
153
|
-
@
|
154
|
-
@shared = []
|
155
|
-
|
104
|
+
_base.rules.each { |k, tests| @rules[k] = tests.clone } unless _base.nil?
|
156
105
|
raise Error.new 'Must provide a default test' if _def_matcher.nil?
|
157
106
|
@def_matcher = _def_matcher
|
158
107
|
@def_resource = _def_resource
|
159
108
|
end
|
160
109
|
|
161
|
-
def always(_upon=nil, &_block)
|
162
|
-
@shared << (_upon || _block)
|
163
|
-
end
|
164
|
-
|
165
|
-
def isolate(_options={}, &_block)
|
166
|
-
@isolated << Profile.new _options.fetch(, @def_matcher), _options.fetch(, @def_resource)
|
167
|
-
end
|
168
|
-
|
169
110
|
## Adds an "allowance" rule
|
170
111
|
def allow(_action, _upon=nil, &_block)
|
171
112
|
@rules[_action] << (_upon || _block)
|
@@ -176,6 +117,11 @@ module Platanus
|
|
176
117
|
# TODO
|
177
118
|
end
|
178
119
|
|
120
|
+
## Clear all rules related to an action
|
121
|
+
def clear(_action)
|
122
|
+
@rules[_action] = []
|
123
|
+
end
|
124
|
+
|
179
125
|
## SHORT HAND METHODS
|
180
126
|
|
181
127
|
def upon(_expr=nil, &_block)
|
data/lib/platanus/stacked2.rb
CHANGED
@@ -6,7 +6,7 @@ module Platanus
|
|
6
6
|
|
7
7
|
## Adds the has_stacked association to an ActiveRecord model.
|
8
8
|
#
|
9
|
-
# TODO
|
9
|
+
# TODO: Investigate how to turn this into an authentic association.
|
10
10
|
#
|
11
11
|
module StackedAttr2
|
12
12
|
|
@@ -28,49 +28,56 @@ module Platanus
|
|
28
28
|
# prepare names
|
29
29
|
tname = _name.to_s
|
30
30
|
tname_single = tname.singularize
|
31
|
-
tname_class = _options.fetch
|
31
|
+
tname_class = _options.fetch :class_name, tname_single.camelize
|
32
|
+
stacked_model = tname_class.constantize
|
33
|
+
prefix = if _options[:cache_prf].nil? then 'last_' else _options.delete(:cache_prf) end # TODO: deprecate?
|
32
34
|
|
33
|
-
#
|
35
|
+
# Generate top_value property
|
36
|
+
#
|
37
|
+
# How this property is generated can vary depending on given parameters or table structure:
|
38
|
+
# * If a top_value_key is provided in options, then a belongs_to association is created using it as foreign key.
|
39
|
+
# * If a top_xxx_id column is present, then a belongs_to association is created using if as foreign key.
|
40
|
+
# * If no key is provided, then a shorcut method that retrieves the stack's top is generated
|
41
|
+
#
|
34
42
|
top_value_prop = "top_#{tname_single}"
|
35
|
-
if _options.has_key? :top_value_key
|
36
|
-
belongs_to top_value_prop.to_sym, class_name: tname_class, foreign_key: _options
|
43
|
+
top_value_key = if _options.has_key? :top_value_key
|
44
|
+
belongs_to top_value_prop.to_sym, class_name: tname_class, foreign_key: _options[:top_value_key], autosave: true
|
45
|
+
_options.delete(:top_value_key)
|
37
46
|
elsif self.column_names.include? "#{top_value_prop}_id"
|
38
|
-
belongs_to top_value_prop.to_sym, class_name: tname_class
|
47
|
+
belongs_to top_value_prop.to_sym, class_name: tname_class, autosave: true
|
48
|
+
"#{top_value_prop}_id"
|
39
49
|
else
|
40
|
-
|
50
|
+
top_value_var = "@_stacked_#{tname}_top".to_sym
|
41
51
|
send :define_method, top_value_prop do
|
42
52
|
# Storing the last stacked value will not prevent race conditions
|
43
53
|
# when simultaneous updates occur.
|
44
|
-
last = instance_variable_get
|
45
|
-
return last unless last.nil?
|
46
|
-
instance_variable_set(
|
47
|
-
end
|
48
|
-
send :define_method, "#{top_value_prop}=" do |_top|
|
49
|
-
instance_variable_set(instance_var, _top)
|
54
|
+
last = instance_variable_get top_value_var
|
55
|
+
return last unless last.nil? or !last.persisted?
|
56
|
+
instance_variable_set(top_value_var, self.send(tname).all.first)
|
50
57
|
end
|
58
|
+
nil
|
51
59
|
end
|
52
|
-
send :private, "#{top_value_prop}="
|
53
60
|
|
54
|
-
|
61
|
+
# When called inside callbacks, returns the new value being put at top of the stack.
|
62
|
+
new_value_var = "@_stacked_#{tname}_new"
|
63
|
+
send :define_method, "#{tname_single}_will" do
|
64
|
+
instance_variable_get(new_value_var)
|
65
|
+
end
|
55
66
|
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
send :define_method, "#{attr_name}" do
|
62
|
-
return changes[attr_name] if changes.has_key? attr_name
|
63
|
-
return self.send(prefix + attr_name) if self.respond_to? prefix + attr_name # Return cached value if avaliable
|
64
|
-
top = self.send top_value_prop
|
65
|
-
return nil if top.nil?
|
66
|
-
return top.send attr_name
|
67
|
-
end
|
68
|
-
attr_accessible attr_name
|
67
|
+
# When called inside callbacks, will return the top value unless a new value is
|
68
|
+
# being pushed, in that case it returns the new value
|
69
|
+
last_value_var = "@_stacked_#{tname}_last"
|
70
|
+
send :define_method, "#{tname_single}_is" do
|
71
|
+
instance_variable_get(last_value_var)
|
69
72
|
end
|
70
73
|
|
71
|
-
#
|
74
|
+
# Prepare cached attributes
|
75
|
+
#
|
76
|
+
# Attribute caching allows the parent model to store the top value for
|
77
|
+
# some of the stacked model attributes (defined in options using the cached key)
|
78
|
+
#
|
72
79
|
to_cache = _options.delete(:cached)
|
73
|
-
|
80
|
+
if to_cache
|
74
81
|
to_cache = to_cache.map do |cache_attr|
|
75
82
|
unless cache_attr.is_a? Hash
|
76
83
|
name = cache_attr.to_s
|
@@ -83,61 +90,137 @@ module Platanus
|
|
83
90
|
end
|
84
91
|
end
|
85
92
|
|
86
|
-
# callbacks
|
87
|
-
|
93
|
+
# register callbacks
|
94
|
+
define_callbacks "stack_#{tname_single}"
|
88
95
|
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
96
|
+
# push logic
|
97
|
+
__update_stack = ->(_ctx, _top, _new_top, _save_quiet, &_block) do
|
98
|
+
begin
|
99
|
+
# make xx_top_value avaliable for event handlers
|
100
|
+
_ctx.instance_variable_set(new_value_var, _top) if _new_top
|
101
|
+
_ctx.instance_variable_set(last_value_var, _top)
|
93
102
|
|
94
|
-
|
95
|
-
|
103
|
+
_ctx.run_callbacks "stack_#{tname_single}" do
|
104
|
+
|
105
|
+
# cache required fields
|
106
|
+
# TODO: improve cache: convention over configuration!
|
107
|
+
# cache should be automatic given certain column names and should include aliased attribues and virtual attributes.
|
108
|
+
# has_stacked :things, cache: { prefix: '', aliases: { xx => xx }, exclude: [], virtual: { xx => xx } }
|
109
|
+
if to_cache
|
110
|
+
to_cache.each do |cache_attr|
|
111
|
+
value = if cache_attr.has_key? :from
|
112
|
+
_top.nil? ? nil : _top.send(cache_attr[:from])
|
113
|
+
else
|
114
|
+
_ctx.send(cache_attr[:virtual])
|
115
|
+
end
|
116
|
+
_ctx.send(cache_attr[:to].to_s + '=', value)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
_block.call if _block
|
121
|
+
|
122
|
+
if _new_top
|
123
|
+
# TODO: this leaves the invalid record on top of the stack and invalid cached values,
|
124
|
+
# maybe validation should ocurr before caching...
|
125
|
+
raise ActiveRecord::RecordInvalid.new(_top) unless _ctx.send(tname) << _top
|
126
|
+
end
|
127
|
+
|
128
|
+
# reset top_value_prop to top
|
129
|
+
if top_value_key
|
130
|
+
if _save_quiet
|
131
|
+
top_id = if _top.nil? then nil else _top.id end
|
132
|
+
if _ctx.send(top_value_key) != top_id
|
133
|
+
_ctx.update_column(top_value_key, top_id)
|
134
|
+
_ctx.send(top_value_prop, false) # reset belongs_to cache
|
135
|
+
end
|
136
|
+
else
|
137
|
+
_ctx.send("#{top_value_prop}=", _top)
|
138
|
+
end
|
139
|
+
else
|
140
|
+
_ctx.instance_variable_set(top_value_var, _top)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
ensure
|
144
|
+
_ctx.instance_variable_set(new_value_var, nil)
|
145
|
+
_ctx.instance_variable_set(last_value_var, nil)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Attribute mirroring
|
150
|
+
#
|
151
|
+
# Mirroring allows using the top value attributes in the parent model,
|
152
|
+
# it also allows modifying the attributes in the parent model, if the model is
|
153
|
+
# then saved, the modified attributes are wrapped in a new stack model object and put
|
154
|
+
# on top.
|
155
|
+
#
|
156
|
+
mirror_cache_var = "@_stacked_#{tname}_mirror".to_sym
|
157
|
+
if _options.delete(:mirroring)
|
158
|
+
stacked_model.accessible_attributes.each do |attr_name|
|
159
|
+
|
160
|
+
unless self.method_defined? "#{attr_name}="
|
161
|
+
send :define_method, "#{attr_name}=" do |value|
|
162
|
+
mirror = instance_variable_get(mirror_cache_var)
|
163
|
+
mirror = instance_variable_set(mirror_cache_var, {}) if mirror.nil?
|
164
|
+
mirror[attr_name] = value
|
165
|
+
end
|
166
|
+
else
|
167
|
+
Rails.logger.warn "stacked: failed to mirror setter for #{attr_name} in #{self.to_s}"
|
168
|
+
end
|
169
|
+
|
170
|
+
unless self.method_defined? attr_name
|
171
|
+
send :define_method, attr_name do
|
172
|
+
mirror = instance_variable_get(mirror_cache_var)
|
173
|
+
return mirror[attr_name] if !mirror.nil? and mirror.has_key? attr_name
|
174
|
+
|
175
|
+
return self.send(prefix + attr_name) if self.respond_to? prefix + attr_name # return cached value if avaliable
|
176
|
+
top = self.send top_value_prop
|
177
|
+
return nil if top.nil?
|
178
|
+
return top.send attr_name
|
179
|
+
end
|
96
180
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
181
|
+
send :define_method, "#{attr_name}_changed?" do
|
182
|
+
mirror = instance_variable_get(mirror_cache_var)
|
183
|
+
return true if !mirror.nil? and mirror.has_key? attr_name
|
184
|
+
return self.send(prefix + attr_name + '_changed?') if self.respond_to? prefix + attr_name + '_changed?' # return cached value if avaliable
|
185
|
+
return true # for now just return true for non cached attributes
|
186
|
+
end
|
187
|
+
|
188
|
+
attr_accessible attr_name
|
103
189
|
else
|
104
|
-
|
190
|
+
Rails.logger.warn "stacked: failed to mirror getter for #{attr_name} in #{self.to_s}"
|
105
191
|
end
|
106
|
-
_ctx.send(cache_attr[:to].to_s + '=', value)
|
107
192
|
end
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
193
|
+
|
194
|
+
# before saving model, load changes from virtual attributes.
|
195
|
+
set_callback :save, :around do |&_block|
|
196
|
+
|
197
|
+
mirror = instance_variable_get(mirror_cache_var)
|
198
|
+
if !mirror.nil? and mirror.count > 0
|
199
|
+
|
200
|
+
# propagate non cached attributes (only if record is not new and there is a top state)
|
201
|
+
unless self.new_record?
|
202
|
+
top = self.send top_value_prop
|
203
|
+
unless top.nil?
|
204
|
+
stacked_model.accessible_attributes.each do |attr_name|
|
205
|
+
mirror[attr_name] = top.send(attr_name) unless mirror.has_key? attr_name
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
obj = stacked_model.new(mirror)
|
211
|
+
instance_variable_set(mirror_cache_var, {}) # reset mirror changes
|
212
|
+
__update_stack.call(self, obj, true, true, &_block)
|
213
|
+
|
214
|
+
else _block.call end
|
128
215
|
end
|
129
216
|
end
|
130
217
|
|
218
|
+
# Push methods
|
219
|
+
|
131
220
|
send :define_method, "push_#{tname_single}!" do |obj|
|
132
221
|
self.class.transaction do
|
133
|
-
|
134
|
-
|
135
|
-
cache_step.call(self, obj, true)
|
136
|
-
self.save! if self.new_record? # make sure there is an id BEFORE pushing
|
137
|
-
raise ActiveRecord::RecordInvalid.new(obj) unless send(tname).send('<<', obj)
|
138
|
-
after_step.call(self, obj)
|
139
|
-
|
140
|
-
self.save! if self.changed? # Must save again, no other way...
|
222
|
+
__update_stack.call(self, obj, true, false) { self.save! if self.new_record? }
|
223
|
+
self.save! if self.changed?
|
141
224
|
end
|
142
225
|
end
|
143
226
|
|
@@ -149,15 +232,13 @@ module Platanus
|
|
149
232
|
end
|
150
233
|
end
|
151
234
|
|
235
|
+
# Restore methods
|
236
|
+
|
152
237
|
send :define_method, "restore_#{tname}!" do
|
153
238
|
self.class.transaction do
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
cache_step.call(self, top, false)
|
158
|
-
after_step.call(self, top)
|
159
|
-
|
160
|
-
self.save! if self.changed?
|
239
|
+
top = self.send(tname).all.first
|
240
|
+
__update_stack.call(self, top, false, false)
|
241
|
+
self.save! if self.changed?
|
161
242
|
end
|
162
243
|
end
|
163
244
|
|
@@ -168,6 +249,13 @@ module Platanus
|
|
168
249
|
return false
|
169
250
|
end
|
170
251
|
end
|
252
|
+
|
253
|
+
# setup main association
|
254
|
+
# TODO: Support other kind of ordering, this would require to reevaluate top on every push
|
255
|
+
_options[:order] = 'created_at DESC, id DESC'
|
256
|
+
_options[:limit] = 1 if _options[:limit].nil?
|
257
|
+
_options.delete(:limit) if _options[:limit] == :no_limit
|
258
|
+
has_many _name, _options
|
171
259
|
end
|
172
260
|
end
|
173
261
|
end
|
data/lib/platanus/version.rb
CHANGED
data/platanus.gemspec
CHANGED
@@ -12,8 +12,9 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
13
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
14
|
gem.name = "platanus"
|
15
|
-
gem.require_paths = ["lib"
|
15
|
+
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Platanus::VERSION
|
17
17
|
|
18
18
|
gem.add_runtime_dependency "multi_json", [">= 1.3.2"]
|
19
|
+
gem.add_development_dependency "rspec"
|
19
20
|
end
|
data/spec/canned2_spec.rb
CHANGED
@@ -17,12 +17,10 @@ describe Platanus::Canned2 do
|
|
17
17
|
class DummyCtx
|
18
18
|
|
19
19
|
attr_reader :params
|
20
|
-
attr_reader :action_name
|
21
20
|
attr_reader :current_user
|
22
21
|
|
23
|
-
def initialize(_user,
|
22
|
+
def initialize(_user, _params={})
|
24
23
|
@current_user = _user
|
25
|
-
@action_name = _action_name
|
26
24
|
@params = _params
|
27
25
|
end
|
28
26
|
end
|
@@ -42,7 +40,6 @@ describe Platanus::Canned2 do
|
|
42
40
|
allow 'rute1#action3', upon { same(:char1, key: "current_user.char1") }
|
43
41
|
allow 'rute1#action4', upon(:current_user) { same(:param2, key: "char2") and checks(:test1) }
|
44
42
|
allow 'rute1#action5', upon(:current_user) { passes { current_user.char2 == params[:param2] } }
|
45
|
-
allow 'rute2', upon(:current_user) { same(:char1) and action_is_not(:create) }
|
46
43
|
|
47
44
|
# Complex routes
|
48
45
|
allow 'rute1#action5' do
|
@@ -52,8 +49,8 @@ describe Platanus::Canned2 do
|
|
52
49
|
end
|
53
50
|
end
|
54
51
|
|
55
|
-
let(:good_ctx) { DummyCtx.new(DummyUsr.new(10, "200"),
|
56
|
-
let(:bad_ctx) { DummyCtx.new(DummyUsr.new(10, 30),
|
52
|
+
let(:good_ctx) { DummyCtx.new(DummyUsr.new(10, "200"), char1: '10', param2: '200') }
|
53
|
+
let(:bad_ctx) { DummyCtx.new(DummyUsr.new(10, 30), char1: '10', param2: '200') }
|
57
54
|
|
58
55
|
describe "._run" do
|
59
56
|
context 'when using single context rules' do
|
@@ -76,9 +73,6 @@ describe Platanus::Canned2 do
|
|
76
73
|
it "does authorize on rute with context and inline test" do
|
77
74
|
Roles.can?(good_ctx, :user, 'rute1#action5').should be_true
|
78
75
|
end
|
79
|
-
it "does not authorize on rute with context, match and action_is_not" do
|
80
|
-
Roles.can?(good_ctx, :user, 'rute2').should be_false
|
81
|
-
end
|
82
76
|
end
|
83
77
|
|
84
78
|
context 'when using multiple context rules' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: platanus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.49
|
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: 2012-
|
12
|
+
date: 2012-12-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 1.3.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
description: Platan.us utility gem
|
31
47
|
email:
|
32
48
|
- ignacio@platan.us
|
@@ -68,7 +84,6 @@ post_install_message:
|
|
68
84
|
rdoc_options: []
|
69
85
|
require_paths:
|
70
86
|
- lib
|
71
|
-
- lib/platanus
|
72
87
|
required_ruby_version: !ruby/object:Gem::Requirement
|
73
88
|
none: false
|
74
89
|
requirements:
|