rapid 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/Rakefile +66 -0
  2. data/lib/rad/http_controller/acts_as/authenticated.rb +131 -0
  3. data/lib/rad/http_controller/acts_as/authenticated_master_domain.rb +119 -0
  4. data/lib/rad/http_controller/acts_as/authorized.rb +83 -0
  5. data/lib/rad/http_controller/acts_as/localized.rb +27 -0
  6. data/lib/rad/http_controller/acts_as/multitenant.rb +53 -0
  7. data/lib/rad/http_controller/helpers/service_mix_helper.rb +50 -0
  8. data/lib/rad/http_controller.rb +15 -0
  9. data/lib/rad/lib/text_utils.rb +334 -0
  10. data/lib/rad/locales/en.yml +80 -0
  11. data/lib/rad/locales/ru.yml +83 -0
  12. data/lib/rad/locales.rb +2 -0
  13. data/lib/rad/models/account.rb +88 -0
  14. data/lib/rad/models/default_permissions.yml +26 -0
  15. data/lib/rad/models/micelaneous.rb +1 -0
  16. data/lib/rad/models/role.rb +88 -0
  17. data/lib/rad/models/secure_token.rb +33 -0
  18. data/lib/rad/models/space.rb +184 -0
  19. data/lib/rad/models/user.rb +158 -0
  20. data/lib/rad/models.rb +41 -0
  21. data/lib/rad/mongo_mapper/acts_as/authenticated_by_open_id.rb +29 -0
  22. data/lib/rad/mongo_mapper/acts_as/authenticated_by_password.rb +120 -0
  23. data/lib/rad/mongo_mapper/acts_as/authorized.rb +197 -0
  24. data/lib/rad/mongo_mapper/acts_as/authorized_object.rb +171 -0
  25. data/lib/rad/mongo_mapper/multitenant.rb +34 -0
  26. data/lib/rad/mongo_mapper/rad_micelaneous.rb +43 -0
  27. data/lib/rad/mongo_mapper/space_keys.rb +62 -0
  28. data/lib/rad/mongo_mapper/text_processor.rb +47 -0
  29. data/lib/rad/mongo_mapper.rb +20 -0
  30. data/lib/rad/paperclip/callbacks.rb +40 -0
  31. data/lib/rad/paperclip/extensions.rb +64 -0
  32. data/lib/rad/paperclip/integration.rb +165 -0
  33. data/lib/rad/paperclip/mime.rb +5 -0
  34. data/lib/rad/paperclip/validations.rb +64 -0
  35. data/lib/rad/paperclip.rb +11 -0
  36. data/lib/rad/spec/controller.rb +51 -0
  37. data/lib/rad/spec/model/factories.rb +65 -0
  38. data/lib/rad/spec/model.rb +85 -0
  39. data/lib/rad/spec/rem_helper.rb +145 -0
  40. data/lib/rad/spec.rb +4 -0
  41. data/lib/rad/tasks/backup.rake +64 -0
  42. data/lib/rad/tasks/initialize.rake +35 -0
  43. data/lib/rad.rb +32 -0
  44. data/readme.md +3 -0
  45. data/spec/controller/authorization_spec.rb +146 -0
  46. data/spec/controller/helper.rb +14 -0
  47. data/spec/lib/helper.rb +7 -0
  48. data/spec/lib/text_utils_spec.rb +238 -0
  49. data/spec/models/authorization_spec.rb +93 -0
  50. data/spec/models/authorized_object_spec.rb +258 -0
  51. data/spec/models/file_audit_spec/100.txt +1 -0
  52. data/spec/models/file_audit_spec/302.txt +3 -0
  53. data/spec/models/file_audit_spec.rb +168 -0
  54. data/spec/models/helper.rb +11 -0
  55. data/spec/models/space_key_spec.rb +68 -0
  56. data/spec/models/user_spec.rb +80 -0
  57. data/spec/mongo_mapper/basic_spec.rb +41 -0
  58. data/spec/mongo_mapper/helper.rb +10 -0
  59. data/spec/spec.opts +4 -0
  60. metadata +138 -0
@@ -0,0 +1,258 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper"
2
+
3
+ describe "Authorized Object" do
4
+ with_rad_model
5
+
6
+ before :all do
7
+ class ::AModel
8
+ include MongoMapper::Document
9
+ plugin MongoMapper::Plugins::AuthorizedObject
10
+ acts_as_authorized_object
11
+ end
12
+
13
+ # MongoMapper.call_deferred
14
+ end
15
+
16
+ after :all do
17
+ Object.send :remove_const, :AModel if Object.const_defined? :AModel
18
+ end
19
+
20
+ before :each do
21
+ set_default_space
22
+ end
23
+
24
+ describe "Owner, Viewers, Collaborators" do
25
+ before :each do
26
+ @user = Factory.create :user, :name => 'auser'
27
+ User.current = nil
28
+ end
29
+
30
+ it "should be abel to create objects (from error)" do
31
+ User.current = @user
32
+ o = AModel.new
33
+ o.save!
34
+ end
35
+
36
+ it "should by default set current user_name as owner_name if there is current user" do
37
+ o = AModel.new
38
+ o.owner_name.should be_nil
39
+
40
+ User.current = @user
41
+ o = AModel.new
42
+ o.owner_name.should == 'auser'
43
+ end
44
+
45
+ it "owner" do
46
+ o = AModel.new
47
+ o.owner = @user
48
+ o.owner_name.should == @user.name
49
+ o.viewers.should == %w{manager user:auser}
50
+ o.should be_valid
51
+ end
52
+
53
+ it 'viewers' do
54
+ o = AModel.new
55
+ o.owner = @user
56
+ o.add_viewer :user
57
+ o.viewers.should == %w{manager member user user:auser}
58
+ o.should be_valid
59
+
60
+ o.remove_viewer :user
61
+ o.viewers.should == %w{manager user:auser}
62
+ o.should be_valid
63
+
64
+ o.add_viewer :member
65
+ o.viewers.should == %w{manager member user:auser}
66
+ o.should be_valid
67
+
68
+ o.add_viewer :user
69
+ o.viewers.should == %w{manager member user user:auser}
70
+ o.should be_valid
71
+ end
72
+
73
+ it "duplicate roles (from error)" do
74
+ o = AModel.new
75
+ o.owner = @user
76
+ o.save!
77
+
78
+ o = AModel.first # don't use reload, it willn't catch this error
79
+ o.viewers.should == %w{manager user:auser}
80
+ end
81
+
82
+ it "collaborators" do
83
+ o = AModel.new
84
+ o.owner = @user
85
+ o.add_collaborator :member
86
+ o.collaborators.should == %w{member}
87
+ o.should be_valid
88
+
89
+ Space.current.custom_roles << 'director'
90
+ o.add_collaborator :director
91
+ o.collaborators.should == %w{member director}
92
+ o.should be_valid
93
+
94
+ o.remove_collaborator :member
95
+ o.collaborators.should == %w{director}
96
+ o.should be_valid
97
+ end
98
+
99
+ it "normalized_collaborators" do
100
+ o = AModel.new
101
+ o.owner = @user
102
+ o.add_collaborator :member
103
+ o.normalized_collaborators.should == %w{manager member user:auser}
104
+ end
105
+
106
+ it "viewers and collaborators dependance" do
107
+ o = AModel.new
108
+ o.owner = @user
109
+ o.add_collaborator :user
110
+ o.collaborators.should == %w{user}
111
+ o.viewers.should == %w{manager member user user:auser}
112
+ o.should be_valid
113
+
114
+ o.remove_viewer :member
115
+ o.viewers.should == %w{manager user user:auser}
116
+ o.collaborators.should == %w{user}
117
+
118
+ o.remove_viewer :user
119
+ o.viewers.should == %w{manager user:auser}
120
+ o.collaborators.should == %w{}
121
+ end
122
+
123
+ it "major viewers" do
124
+ o = AModel.new
125
+ o.owner = @user
126
+ o.add_viewer :member
127
+ o.add_viewer :director
128
+ o.viewers.should == %w{director manager member user:auser}
129
+ o.minor_viewers.should == %w{director member user:auser}
130
+ end
131
+
132
+ it "collaborators should be able to change object (from error)" do
133
+ col = Factory.create :member, :name => 'collaborator'
134
+
135
+ o = AModel.new
136
+ o.owner = @user
137
+ col.can?(:update, o).should be_false
138
+
139
+ o.add_collaborator :member
140
+ o.clear_cache
141
+ col.can?(:update, o).should be_true
142
+ @user.can?(:update, o).should be_true
143
+
144
+ o.save!
145
+ o = AModel.find o.id
146
+ col.can?(:update, o).should be_true
147
+ @user.can?(:update, o).should be_true
148
+ end
149
+ end
150
+
151
+ describe "Permissions" do
152
+ before :each do
153
+ Space.stub!(:permissions).and_return('manage' => %w{manager})
154
+ end
155
+
156
+ describe "General" do
157
+ before :each do
158
+ Space.stub!(:permissions).and_return('manage' => %w{manager})
159
+ end
160
+
161
+ it "should also works without mulititenancy" do
162
+ Space.current = nil
163
+ Account.current = nil
164
+ u = User.new
165
+ u.can?(:manage, AModel)
166
+ end
167
+
168
+ it "should works for new user" do
169
+ u = User.new
170
+ u.can?(:manage, AModel)
171
+ end
172
+
173
+ it 'permissions' do
174
+ u = Factory.create :user
175
+ u.can?(:manage, AModel).should be_false
176
+
177
+ u = Factory.create :manager
178
+ u.can?(:manage, AModel).should be_true
179
+ end
180
+ end
181
+
182
+ describe "as Owner" do
183
+ before :each do
184
+ Space.stub!(:permissions).and_return('manage' => %w{manager owner})
185
+ @user = Factory.create :user
186
+
187
+ @object = AModel.new
188
+
189
+ @owned_object = AModel.new
190
+ @owned_object.owner = @user
191
+ end
192
+
193
+ it "owner?" do
194
+ @user.should_not be_owner(@object)
195
+ @user.should be_owner(@owned_object)
196
+ end
197
+
198
+ it 'permissions for owner' do
199
+ @user.can?(:manage, @object).should be_false
200
+ @user.can?(:manage, @owned_object).should be_true
201
+ end
202
+ end
203
+
204
+ describe "Special :view permission" do
205
+ before :each do
206
+ Space.stub!(:permissions).and_return('view' => %w{manager}) # managers can see anything, always, it's hardcoded
207
+ end
208
+
209
+ it "user (public) viewers" do
210
+ user = Factory.create :user
211
+
212
+ o = AModel.new
213
+ o.stub!(:viewers){%w{user}}
214
+
215
+ user.can?(:view, o).should be_true
216
+ end
217
+
218
+ it "member viewers" do
219
+ Space.stub!(:permissions).and_return('view' => [])
220
+
221
+ user = Factory.create :user
222
+ member = Factory.create :member
223
+ manager = Factory.create :manager
224
+
225
+ o = AModel.new
226
+ o.stub!(:viewers).and_return(%w{member manager})
227
+
228
+ user.can?(:view, o).should be_false
229
+ member.can?(:view, o).should be_true
230
+ manager.can?(:view, o).should be_true
231
+ end
232
+
233
+ it "owner (private) viewers" do
234
+ Space.stub!(:permissions).and_return('view' => [])
235
+
236
+ owner = Factory.create :user, :name => "aname"
237
+ user = Factory.create :user
238
+ member = Factory.create :member
239
+ manager = Factory.create :manager
240
+
241
+ o = AModel.new
242
+ o.stub!(:owner_name){owner.name}
243
+ o.stub!(:viewers){%w{user:aname manager}}
244
+
245
+ owner.can?(:view, o).should be_true
246
+ user.can?(:view, o).should be_false
247
+ member.can?(:view, o).should be_false
248
+ manager.can?(:view, o).should be_true
249
+ end
250
+
251
+ it "should correct works with non authorized objects (from error)" do
252
+ user = Factory.create :user
253
+ user.can?(:view, Object.new).should be_false
254
+ end
255
+ end
256
+
257
+ end
258
+ end
@@ -0,0 +1 @@
1
+ 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
@@ -0,0 +1,3 @@
1
+ 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
2
+ 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
3
+ 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
@@ -0,0 +1,168 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper"
2
+
3
+ describe "File Audit" do
4
+ with_rad_model
5
+
6
+ DATA_DIR = "#{File.dirname(__FILE__)}/file_audit_spec"
7
+
8
+ def open_small_file &block
9
+ File.open("#{DATA_DIR}/100.txt", &block)
10
+ end
11
+
12
+ def open_big_file &block
13
+ File.open("#{DATA_DIR}/302.txt", &block)
14
+ end
15
+
16
+ before :all do
17
+ class ::Amodel
18
+ include MongoMapper::Document
19
+ include Paperclip
20
+
21
+ interpolation = "/system/:account/:space/:class/:id/:attachment"
22
+ has_attached_file :data,
23
+ :path => (":crystal_root/public" + interpolation),
24
+ :url => (":crystal_root/public" + interpolation)
25
+
26
+ validates_file :data
27
+ trace_file :data
28
+
29
+ def disable_file_audit?
30
+ false
31
+ end
32
+ end
33
+
34
+ Paperclip.logger = Logger.new nil
35
+ end
36
+
37
+ after :all do
38
+ Object.send :remove_const, :Amodel if Object.const_defined? :Amodel
39
+ end
40
+
41
+ before :each do
42
+ set_default_space
43
+ User.current = @user = Factory.create(:user)
44
+ end
45
+
46
+ it "should check max space files size" do
47
+ Account.current.max_account_files_size = 200
48
+ m = Amodel.new
49
+
50
+ open_small_file do |f|
51
+ m.data = f
52
+ m.should be_valid
53
+ end
54
+
55
+ open_big_file do |f|
56
+ m.data = f
57
+ m.should_not be_valid
58
+ end
59
+ end
60
+
61
+ it "shouldn't count the same file when model updated" do
62
+ # assign file
63
+ m = nil
64
+ open_small_file{|f| m = Amodel.create :data => f}
65
+ m.data_file_size.should == 100
66
+ m.data_old_file_size.should == 100
67
+ User.current.files_size.should == 100
68
+ Account.current.files_size.should == 100
69
+
70
+ m.save!
71
+ m.data_file_size.should == 100
72
+ m.data_old_file_size.should == 100
73
+ User.current.files_size.should == 100
74
+ Account.current.files_size.should == 100
75
+ end
76
+
77
+ it "should check max user files size" do
78
+ Space.current.max_user_files_size = 200
79
+ m = Amodel.new
80
+
81
+ open_small_file do |f|
82
+ m.data = f
83
+ m.should be_valid
84
+ end
85
+
86
+ open_big_file do |f|
87
+ m.data = f
88
+ m.should_not be_valid
89
+ end
90
+ end
91
+
92
+ it "should check max file size" do
93
+ Account.current.max_file_size = 200
94
+ m = Amodel.new
95
+
96
+ open_small_file do |f|
97
+ m.data = f
98
+ m.should be_valid
99
+ end
100
+
101
+ open_big_file do |f|
102
+ m.data = f
103
+ m.should_not be_valid
104
+ end
105
+ end
106
+
107
+ it "should also decrease files size" do
108
+ # assign file
109
+ m, m2 = nil
110
+ open_small_file{|f| m = Amodel.create :data => f}
111
+ open_big_file{|f| m2 = Amodel.create :data => f}
112
+
113
+ User.current.files_size.should == 402
114
+ Account.current.files_size.should == 402
115
+
116
+ # destroying file
117
+ m2.destroy
118
+ User.current.files_size.should == 100
119
+ Account.current.files_size.should == 100
120
+ User.current.reload; Account.current.reload
121
+ User.current.files_size.should == 100
122
+ Account.current.files_size.should == 100
123
+ end
124
+
125
+ it "shoud trace file size" do
126
+ # assign file
127
+ m = nil
128
+ open_small_file{|f| m = Amodel.create :data => f}
129
+
130
+ m.data_file_size.should == 100
131
+ m.data_old_file_size.should == 100
132
+ User.current.files_size.should == 100
133
+ Account.current.files_size.should == 100
134
+ User.current.reload; Account.current.reload
135
+ User.current.files_size.should == 100
136
+ Account.current.files_size.should == 100
137
+
138
+ # update file
139
+ open_big_file{|f| m.data = f; m.save!}
140
+
141
+ m.data_file_size.should == 302
142
+ m.data_old_file_size.should == 302
143
+ User.current.files_size.should == 302
144
+ Account.current.files_size.should == 302
145
+ User.current.reload; Account.current.reload
146
+ User.current.files_size.should == 302
147
+ Account.current.files_size.should == 302
148
+
149
+ # assign another file
150
+ m2 = nil
151
+ open_small_file{|f| m2 = Amodel.create :data => f}
152
+
153
+ m2.data_file_size.should == 100
154
+ m2.data_old_file_size.should == 100
155
+ User.current.files_size.should == 402
156
+ Account.current.files_size.should == 402
157
+
158
+ # another user
159
+ User.current = Factory.create :user
160
+ m3 = nil
161
+ open_small_file{|f| m3 = Amodel.create :data => f}
162
+
163
+ User.current.files_size.should == 100
164
+ @user.files_size.should == 402
165
+ Account.current.files_size.should == 502
166
+ end
167
+
168
+ end
@@ -0,0 +1,11 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ lib_dir = File.expand_path("#{dir}/../../../../lib")
3
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include? lib_dir
4
+
5
+ # models
6
+ require 'mongo_mapper'
7
+ require 'mongo_mapper_ext'
8
+
9
+ # spec & helpers
10
+ require 'spec_ext'
11
+ require 'rad/spec/model'
@@ -0,0 +1,68 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper"
2
+
3
+ describe "SpaceKeys" do
4
+ with_rad_model
5
+
6
+ before :all do
7
+ class ::AnUser
8
+ include MongoMapper::Document
9
+ plugin MongoMapper::Plugins::SpaceKeys
10
+
11
+ space_key :roles, Array
12
+ end
13
+ # MongoMapper.call_deferred
14
+ end
15
+
16
+ after :all do
17
+ Object.send :remove_const, :AnUser if Object.const_defined? :AnUser
18
+ end
19
+
20
+ before :each do
21
+ set_default_space
22
+ @user = AnUser.new
23
+ end
24
+
25
+ it "shouldn't save empty containers" do
26
+ @user.roles
27
+ @user.space_keys_containers.size.should == 0
28
+ end
29
+
30
+ it "should correctly save" do
31
+ @user.roles = ['manager']
32
+ @user.space_keys_containers.size.should == 1
33
+ @user.save!
34
+
35
+ @user = AnUser.find(@user.id)
36
+ @user.roles.should == ['manager']
37
+ end
38
+
39
+ it "should clean empty containers" do
40
+ @user.roles = ['manager']
41
+ @user.space_keys_containers.size.should == 1
42
+ @user.roles = []
43
+ @user.space_keys_containers.size.should == 0
44
+ end
45
+
46
+ it "should clean empty containers for saved objects" do
47
+ @user.roles = ['manager']
48
+ @user.save!
49
+
50
+ @user = AnUser.find @user.id
51
+ @user.roles = []
52
+ @user.space_keys_containers.size.should == 0
53
+ end
54
+
55
+ it "should works with many spaces" do
56
+ @user.roles = ['manager']
57
+
58
+ previous_space = Space.current
59
+ space = Factory.build :space
60
+ Space.current = space
61
+
62
+ @user.roles.should == []
63
+ @user.roles = ['user']
64
+
65
+ Space.current = previous_space
66
+ @user.roles.should == ['manager']
67
+ end
68
+ end
@@ -0,0 +1,80 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper"
2
+
3
+ describe "User" do
4
+ with_rad_model
5
+
6
+ it "blank user" do
7
+ user = Factory.build(:blank_user)
8
+ user.should_not be_valid
9
+ user.should be_inactive
10
+ end
11
+
12
+ describe "Authentication by Password" do
13
+ it "registering user" do
14
+ user = Factory.build(:new_user)
15
+ user.crypted_password.should_not be_blank
16
+ user.should be_valid
17
+ user.should be_inactive
18
+ user.should be_authenticated_by_password(user.password)
19
+ end
20
+
21
+ it "authentication" do
22
+ user = Factory.create :user
23
+ User.authenticate_by_password(user.name, user.password).should == user
24
+ end
25
+
26
+ it "email uniquiness" do
27
+ user = Factory.build :user
28
+ user.email = "some@email.com"
29
+ user.save.should be_true
30
+
31
+ user = Factory.build :user
32
+ user.email = "some@email.com"
33
+ user.save.should be_false
34
+ user.errors.on(:email).should_not be_blank
35
+ end
36
+
37
+ it "update_password" do
38
+ user = Factory.create :user
39
+ user.update_password('new_password', 'new_password', 'invalid').should be_false
40
+ user.update_password('new_password', 'new_password', user.password).should be_true
41
+ end
42
+ end
43
+
44
+ describe "Authentication by OpenID" do
45
+ it "registering user" do
46
+ user = Factory.build(:blank_user)
47
+ user.open_ids << "open_id"
48
+ user.crypted_password.should be_blank
49
+ user.should be_valid
50
+ user.should be_inactive
51
+
52
+ user.save.should be_true
53
+ user.reload
54
+ user.crypted_password.should be_blank
55
+ end
56
+
57
+ it "authentication" do
58
+ user = Factory.create :open_id_user
59
+ User.authenticate_by_open_id(user.open_ids.first).should == user
60
+ end
61
+
62
+ it "open_id uniquiness" do
63
+ user = Factory.build :open_id_user
64
+ user.open_ids = ['some_id']
65
+ user.save.should be_true
66
+
67
+ user = Factory.build :open_id_user
68
+ user.open_ids = ['some_id']
69
+ user.save.should be_false
70
+ user.errors.on(:open_ids).should_not be_blank
71
+ end
72
+ end
73
+
74
+ it "add password to OpenID" do
75
+ user = Factory.create :open_id_user
76
+ user.update_password('new_password', 'new_password', '').should be_true
77
+ user.save.should be_true
78
+ User.authenticate_by_password(user.name, user.password).should == user
79
+ end
80
+ end
@@ -0,0 +1,41 @@
1
+ require "#{File.dirname(__FILE__)}/helper"
2
+
3
+ describe "Rad MongoMapper basics" do
4
+ before :all do
5
+ MongoMapper.database = 'test'
6
+ end
7
+
8
+ after :all do
9
+ %w{ToRsonTest}.each{|n| Object.send :remove_const, n if Object.const_defined? n}
10
+ end
11
+
12
+ it "to_rson" do
13
+ class ToRsonTest
14
+ include MongoMapper::Document
15
+ plugin MongoMapper::Plugins::RadMicelaneous
16
+
17
+ key :name, String
18
+ key :text, String
19
+ validates_presence_of :name
20
+ end
21
+ ToRsonTest.delete_all
22
+
23
+ ToRsonTest.create! :name => 'a'
24
+ ToRsonTest.create! :name => 'b'
25
+
26
+ # model.to_rson
27
+ o = ToRsonTest.first
28
+ r = o.to_rson
29
+ r.delete 'id'
30
+ r.should == {"name" => "a", "text" => "null"}
31
+
32
+ o.to_rson(:only => :name).should == {"name" => "a"}
33
+
34
+ # collections.to_rson
35
+ r = ToRsonTest.all.to_rson
36
+ r.collect!{|h| h.delete('id'); h}
37
+ r.should == [{"name" => "a", "text" => "null"}, {"name" => "b", "text" => "null"}]
38
+
39
+ ToRsonTest.all.to_rson(:only => :name).should == [{"name" => "a"}, {"name" => "b"}]
40
+ end
41
+ end
@@ -0,0 +1,10 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ lib_dir = File.expand_path("#{dir}/../../../lib")
3
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include? lib_dir
4
+
5
+ require 'crystal/support'
6
+ require 'mongo_mapper'
7
+ require 'rad/mongo_mapper/rad_micelaneous'
8
+
9
+ # spec & helpers
10
+ require 'spec_ext'
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse