inherited_resources 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG +6 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +86 -0
  5. data/README.rdoc +91 -37
  6. data/Rakefile +0 -22
  7. data/inherited_resources.gemspec +24 -0
  8. data/lib/inherited_resources.rb +7 -2
  9. data/lib/inherited_resources/actions.rb +4 -4
  10. data/lib/inherited_resources/base.rb +11 -10
  11. data/lib/inherited_resources/base_helpers.rb +32 -3
  12. data/lib/inherited_resources/class_methods.rb +102 -10
  13. data/lib/inherited_resources/shallow_helpers.rb +87 -0
  14. data/lib/inherited_resources/url_helpers.rb +48 -2
  15. data/lib/inherited_resources/version.rb +1 -1
  16. data/test/aliases_test.rb +1 -1
  17. data/test/association_chain_test.rb +1 -1
  18. data/test/base_test.rb +16 -8
  19. data/test/belongs_to_test.rb +44 -2
  20. data/test/belongs_to_with_shallow_test.rb +93 -0
  21. data/test/class_methods_test.rb +6 -4
  22. data/test/customized_base_test.rb +1 -1
  23. data/test/customized_belongs_to_test.rb +2 -2
  24. data/test/customized_redirect_to_test.rb +32 -0
  25. data/test/defaults_test.rb +117 -11
  26. data/test/locales/en.yml +17 -0
  27. data/test/nested_belongs_to_test.rb +1 -1
  28. data/test/nested_belongs_to_with_shallow_test.rb +123 -0
  29. data/test/nested_model_with_shallow_test.rb +133 -0
  30. data/test/optional_belongs_to_test.rb +4 -4
  31. data/test/polymorphic_test.rb +5 -5
  32. data/test/redirect_to_test.rb +1 -1
  33. data/test/singleton_test.rb +5 -2
  34. data/test/test_helper.rb +8 -24
  35. data/test/url_helpers_test.rb +243 -2
  36. data/test/views/cars/edit.html.erb +1 -0
  37. data/test/views/cars/index.html.erb +1 -0
  38. data/test/views/cars/new.html.erb +1 -0
  39. data/test/views/cars/show.html.erb +1 -0
  40. data/test/views/cities/edit.html.erb +1 -0
  41. data/test/views/cities/index.html.erb +1 -0
  42. data/test/views/cities/new.html.erb +1 -0
  43. data/test/views/cities/show.html.erb +1 -0
  44. data/test/views/comments/edit.html.erb +1 -0
  45. data/test/views/comments/index.html.erb +1 -0
  46. data/test/views/comments/new.html.erb +1 -0
  47. data/test/views/comments/show.html.erb +1 -0
  48. data/test/views/educations/new.html.erb +0 -0
  49. data/test/views/employees/edit.html.erb +1 -0
  50. data/test/views/employees/index.html.erb +1 -0
  51. data/test/views/employees/new.html.erb +1 -0
  52. data/test/views/employees/show.html.erb +1 -0
  53. data/test/views/groups/edit.html.erb +0 -0
  54. data/test/views/managers/edit.html.erb +1 -0
  55. data/test/views/managers/new.html.erb +1 -0
  56. data/test/views/managers/show.html.erb +1 -0
  57. data/test/views/painters/edit.html.erb +1 -0
  58. data/test/views/painters/index.html.erb +1 -0
  59. data/test/views/painters/new.html.erb +1 -0
  60. data/test/views/painters/show.html.erb +1 -0
  61. data/test/views/pets/edit.html.erb +1 -0
  62. data/test/views/pets/index.html.erb +1 -0
  63. data/test/views/pets/new.html.erb +1 -0
  64. data/test/views/pets/show.html.erb +1 -0
  65. data/test/views/plates/edit.html.erb +1 -0
  66. data/test/views/plates/index.html.erb +1 -0
  67. data/test/views/plates/new.html.erb +1 -0
  68. data/test/views/plates/show.html.erb +1 -0
  69. data/test/views/products/edit.html.erb +1 -0
  70. data/test/views/products/index.html.erb +1 -0
  71. data/test/views/products/new.html.erb +1 -0
  72. data/test/views/products/show.html.erb +1 -0
  73. data/test/views/professors/edit.html.erb +1 -0
  74. data/test/views/professors/index.html.erb +1 -0
  75. data/test/views/professors/new.html.erb +1 -0
  76. data/test/views/professors/show.html.erb +1 -0
  77. data/test/views/projects/index.html.erb +1 -0
  78. data/test/views/projects/index.json.erb +1 -0
  79. data/test/views/projects/respond_to_skip_default_template.html.erb +1 -0
  80. data/test/views/projects/respond_with_resource.html.erb +1 -0
  81. data/test/views/students/edit.html.erb +1 -0
  82. data/test/views/students/new.html.erb +1 -0
  83. data/test/views/tags/edit.html.erb +1 -0
  84. data/test/views/tags/index.html.erb +1 -0
  85. data/test/views/tags/new.html.erb +1 -0
  86. data/test/views/tags/show.html.erb +1 -0
  87. data/test/views/trees/edit.html.erb +1 -0
  88. data/test/views/trees/index.html.erb +1 -0
  89. data/test/views/trees/new.html.erb +1 -0
  90. data/test/views/trees/show.html.erb +1 -0
  91. data/test/views/university/professors/edit.html.erb +1 -0
  92. data/test/views/university/professors/index.html.erb +1 -0
  93. data/test/views/university/professors/new.html.erb +1 -0
  94. data/test/views/university/professors/show.html.erb +1 -0
  95. data/test/views/users/create.js.erb +1 -0
  96. data/test/views/users/destroy.js.erb +1 -0
  97. data/test/views/users/edit.html.erb +1 -0
  98. data/test/views/users/index.html.erb +1 -0
  99. data/test/views/users/new.html.erb +1 -0
  100. data/test/views/users/show.html.erb +1 -0
  101. data/test/views/users/update.js.erb +1 -0
  102. metadata +180 -9
@@ -20,7 +20,7 @@ module InheritedResources
20
20
  # end
21
21
  #
22
22
  def collection
23
- get_collection_ivar || set_collection_ivar(end_of_association_chain.find(:all))
23
+ get_collection_ivar || set_collection_ivar(end_of_association_chain.all)
24
24
  end
25
25
 
26
26
  # This is how the resource is loaded.
@@ -38,7 +38,7 @@ module InheritedResources
38
38
  # probably render a 500 error message.
39
39
  #
40
40
  def resource
41
- get_resource_ivar || set_resource_ivar(end_of_association_chain.find(params[:id]))
41
+ get_resource_ivar || set_resource_ivar(end_of_association_chain.send(method_for_find, params[:id]))
42
42
  end
43
43
 
44
44
  # This method is responsable for building the object on :new and :create
@@ -46,7 +46,7 @@ module InheritedResources
46
46
  # instance variable.
47
47
  #
48
48
  def build_resource
49
- get_resource_ivar || set_resource_ivar(end_of_association_chain.send(method_for_build, params[resource_instance_name] || {}))
49
+ get_resource_ivar || set_resource_ivar(end_of_association_chain.send(method_for_build, params[resource_request_name] || {}))
50
50
  end
51
51
 
52
52
  # Responsible for saving the resource on :create method. Overwriting this
@@ -155,6 +155,10 @@ module InheritedResources
155
155
  self.resources_configuration[:self][:instance_name]
156
156
  end
157
157
 
158
+ def resource_request_name
159
+ self.resources_configuration[:self][:request_name]
160
+ end
161
+
158
162
  # This methods gets your begin_of_association_chain, join it with your
159
163
  # parents chain and returns the scoped association.
160
164
  #
@@ -195,6 +199,11 @@ module InheritedResources
195
199
  resource_collection_name
196
200
  end
197
201
 
202
+ # Returns finder method for instantiate resource by params[:id]
203
+ def method_for_find
204
+ resources_configuration[:self][:finder] || :find
205
+ end
206
+
198
207
  # Get resource ivar based on the current resource controller.
199
208
  #
200
209
  def get_resource_ivar #:nodoc:
@@ -265,6 +274,26 @@ module InheritedResources
265
274
  []
266
275
  end
267
276
 
277
+ # URL to redirect to when redirect implies resource url.
278
+ def smart_resource_url
279
+ url = nil
280
+ if respond_to? :show
281
+ url = resource_url rescue nil
282
+ end
283
+ url ||= smart_collection_url
284
+ end
285
+
286
+ # URL to redirect to when redirect implies collection url.
287
+ def smart_collection_url
288
+ url = nil
289
+ if respond_to? :index
290
+ url ||= collection_url rescue nil
291
+ end
292
+ if respond_to? :parent
293
+ url ||= parent_url rescue nil
294
+ end
295
+ url ||= root_url rescue nil
296
+ end
268
297
  end
269
298
  end
270
299
 
@@ -30,22 +30,31 @@ module InheritedResources
30
30
  #
31
31
  # * <tt>:singleton</tt> - Tells if this controller is singleton or not.
32
32
  #
33
+ # * <tt>:finder</tt> - Specifies which method should be called to instantiate the resource.
34
+ #
35
+ # defaults :project, :finder => :find_by_slug
36
+ #
33
37
  def defaults(options)
34
38
  raise ArgumentError, 'Class method :defaults expects a hash of options.' unless options.is_a? Hash
35
39
 
36
40
  options.symbolize_keys!
37
41
  options.assert_valid_keys(:resource_class, :collection_name, :instance_name,
38
42
  :class_name, :route_prefix, :route_collection_name,
39
- :route_instance_name, :singleton)
43
+ :route_instance_name, :singleton, :finder)
40
44
 
41
- self.resource_class = options.delete(:resource_class) if options.key?(:resource_class)
42
- self.resource_class = options.delete(:class_name).constantize if options.key?(:class_name)
45
+ self.resource_class = options[:resource_class] if options.key?(:resource_class)
46
+ self.resource_class = options[:class_name].constantize if options.key?(:class_name)
43
47
 
44
48
  acts_as_singleton! if options.delete(:singleton)
45
49
 
46
50
  config = self.resources_configuration[:self]
47
51
  config[:route_prefix] = options.delete(:route_prefix) if options.key?(:route_prefix)
48
52
 
53
+ if options.key?(:resource_class) or options.key?(:class_name)
54
+ config[:request_name] = self.resource_class.to_s.underscore.gsub('/', '_')
55
+ options.delete(:resource_class) and options.delete(:class_name)
56
+ end
57
+
49
58
  options.each do |key, value|
50
59
  config[key] = value.to_sym
51
60
  end
@@ -132,9 +141,10 @@ module InheritedResources
132
141
  options.symbolize_keys!
133
142
  options.assert_valid_keys(:class_name, :parent_class, :instance_name, :param,
134
143
  :finder, :route_name, :collection_name, :singleton,
135
- :polymorphic, :optional)
144
+ :polymorphic, :optional, :shallow)
136
145
 
137
146
  optional = options.delete(:optional)
147
+ shallow = options.delete(:shallow)
138
148
  singleton = options.delete(:singleton)
139
149
  polymorphic = options.delete(:polymorphic)
140
150
  finder = options.delete(:finder)
@@ -143,6 +153,7 @@ module InheritedResources
143
153
 
144
154
  acts_as_singleton! if singleton
145
155
  acts_as_polymorphic! if polymorphic || optional
156
+ acts_as_shallow! if shallow
146
157
 
147
158
  raise ArgumentError, 'You have to give me at least one association name.' if symbols.empty?
148
159
  raise ArgumentError, 'You cannot define multiple associations with options: #{options.keys.inspect} to belongs to.' unless symbols.size == 1 || options.empty?
@@ -158,6 +169,8 @@ module InheritedResources
158
169
  self.parents_symbols << symbol
159
170
  end
160
171
 
172
+ self.resources_configuration[:self][:shallow] = true if shallow
173
+
161
174
  config = self.resources_configuration[symbol] = {}
162
175
 
163
176
  config[:parent_class] = options.delete(:parent_class) || begin
@@ -180,6 +193,7 @@ module InheritedResources
180
193
  else
181
194
  create_resources_url_helpers!
182
195
  end
196
+ helper_method :parent, :parent?
183
197
  end
184
198
  alias :nested_belongs_to :belongs_to
185
199
 
@@ -207,6 +221,40 @@ module InheritedResources
207
221
  belongs_to(*symbols << options, &block)
208
222
  end
209
223
 
224
+ # Defines custom restful actions by resource or collection basis.
225
+ #
226
+ # custom_actions :resource => [:delete, :transit], :collection => :search
227
+ #
228
+ # == Options
229
+ #
230
+ # * <tt>:resource</tt> - Allows you to specify resource actions.
231
+ # custom_actions :resource => :delete
232
+ # This macro creates 'delete' method in controller and defines
233
+ # delete_reource_{path,url} helpers. The body of generated 'delete'
234
+ # method is same as 'show' method. So you can override it if need
235
+ #
236
+ # * <tt>:collection</tt> - Allows you to specify collection actions.
237
+ # custom_actions :collection => :search
238
+ # This macro creates 'search' method in controller and defines
239
+ # search_reources_{path,url} helpers. The body of generated 'search'
240
+ # method is same as 'index' method. So you can override it if need
241
+ #
242
+ def custom_actions(options)
243
+ self.resources_configuration[:self][:custom_actions] = options
244
+ options.each do | resource_or_collection, actions |
245
+ [*actions].each do | action |
246
+ create_custom_action(resource_or_collection, action)
247
+ end
248
+ end
249
+ create_resources_url_helpers!
250
+ [*options[:resource]].each do | action |
251
+ helper_method "#{action}_resource_path", "#{action}_resource_url"
252
+ end
253
+ [*options[:collection]].each do | action |
254
+ helper_method "#{action}_resources_path", "#{action}_resources_url"
255
+ end
256
+ end
257
+
210
258
  private
211
259
 
212
260
  def acts_as_singleton! #:nodoc:
@@ -220,15 +268,35 @@ module InheritedResources
220
268
  def acts_as_polymorphic! #:nodoc:
221
269
  unless self.parents_symbols.include?(:polymorphic)
222
270
  include PolymorphicHelpers
223
- helper_method :parent, :parent_type, :parent_class, :parent?
271
+ helper_method :parent_type, :parent_class
224
272
  end
225
273
  end
226
274
 
275
+ def acts_as_shallow! #:nodoc:
276
+ include ShallowHelpers
277
+ end
278
+
227
279
  # Initialize resources class accessors and set their default values.
228
280
  #
229
281
  def initialize_resources_class_accessors! #:nodoc:
230
- # Initialize resource class
282
+ # First priority is the namespaced modek, e.g. User::Group
231
283
  self.resource_class = begin
284
+ namespaced_class = self.name.sub(/Controller/, '').singularize
285
+ namespaced_class.constantize
286
+ rescue NameError
287
+ nil
288
+ end
289
+
290
+ # Second priority the camelcased c, i.e. UserGroup
291
+ self.resource_class ||= begin
292
+ camelcased_class = self.name.sub(/Controller/, '').gsub('::', '').singularize
293
+ camelcased_class.constantize
294
+ rescue NameError
295
+ nil
296
+ end
297
+
298
+ # Otherwise use the Group class, or fail
299
+ self.resource_class ||= begin
232
300
  class_name = self.controller_name.classify
233
301
  class_name.constantize
234
302
  rescue NameError => e
@@ -236,9 +304,16 @@ module InheritedResources
236
304
  nil
237
305
  end
238
306
 
307
+ self.parents_symbols = self.parents_symbols.try(:dup) || []
308
+
239
309
  # Initialize resources configuration hash
240
- self.resources_configuration ||= {}
241
- config = self.resources_configuration[:self] = {}
310
+ self.resources_configuration = self.resources_configuration.try(:dup) || {}
311
+ self.resources_configuration.each do |key, value|
312
+ next unless value.is_a?(Hash) || value.is_a?(Array)
313
+ self.resources_configuration[key] = value.dup
314
+ end
315
+
316
+ config = (self.resources_configuration[:self] ||= {})
242
317
  config[:collection_name] = self.controller_name.to_sym
243
318
  config[:instance_name] = self.controller_name.singularize.to_sym
244
319
 
@@ -249,9 +324,26 @@ module InheritedResources
249
324
  namespaces = self.controller_path.split('/')[0..-2]
250
325
  config[:route_prefix] = namespaces.join('_') unless namespaces.empty?
251
326
 
327
+ # Deal with default request parameters in namespaced controllers, e.g.
328
+ # Forum::Thread#create will properly pick up the request parameter
329
+ # which will be forum_thread, and not thread
330
+ # Additionally make this work orthogonally with instance_name
331
+ config[:request_name] = self.name.sub(/Controller$/, '').underscore.gsub('/', '_').singularize
332
+
252
333
  # Initialize polymorphic, singleton, scopes and belongs_to parameters
253
- self.parents_symbols ||= []
254
- self.resources_configuration[:polymorphic] ||= { :symbols => [], :optional => false }
334
+ polymorphic = self.resources_configuration[:polymorphic] || { :symbols => [], :optional => false }
335
+ polymorphic[:symbols] = polymorphic[:symbols].dup
336
+ self.resources_configuration[:polymorphic] = polymorphic
337
+ end
338
+
339
+ def create_custom_action(resource_or_collection, action)
340
+ class_eval <<-CUSTOM_ACTION, __FILE__, __LINE__
341
+ def #{action}(options={}, &block)
342
+ respond_with(*(with_chain(#{resource_or_collection}) << options), &block)
343
+ end
344
+ alias :#{action}! :#{action}
345
+ protected :#{action}!
346
+ CUSTOM_ACTION
255
347
  end
256
348
 
257
349
  # Hook called on inheritance.
@@ -0,0 +1,87 @@
1
+ module InheritedResources
2
+ # = belongs_to
3
+ #
4
+ # Let's suppose that we have some tasks that belongs to projects. To specify
5
+ # this assoication in your controllers, just do:
6
+ #
7
+ # class TasksController < InheritedResources::Base
8
+ # belongs_to :project
9
+ # end
10
+ #
11
+ # belongs_to accepts several options to be able to configure the association.
12
+ # For example, if you want urls like /projects/:project_title/tasks, you
13
+ # can customize how InheritedResources find your projects:
14
+ #
15
+ # class TasksController < InheritedResources::Base
16
+ # belongs_to :project, :finder => :find_by_title!, :param => :project_title
17
+ # end
18
+ #
19
+ # It also accepts :route_name, :parent_class and :instance_name as options.
20
+ # Check the lib/inherited_resources/class_methods.rb for more.
21
+ #
22
+ # = nested_belongs_to
23
+ #
24
+ # Now, our Tasks get some Comments and you need to nest even deeper. Good
25
+ # practices says that you should never nest more than two resources, but sometimes
26
+ # you have to for security reasons. So this is an example of how you can do it:
27
+ #
28
+ # class CommentsController < InheritedResources::Base
29
+ # nested_belongs_to :project, :task
30
+ # end
31
+ #
32
+ # If you need to configure any of these belongs to, you can nested them using blocks:
33
+ #
34
+ # class CommentsController < InheritedResources::Base
35
+ # belongs_to :project, :finder => :find_by_title!, :param => :project_title do
36
+ # belongs_to :task
37
+ # end
38
+ # end
39
+ #
40
+ # Warning: calling several belongs_to is the same as nesting them:
41
+ #
42
+ # class CommentsController < InheritedResources::Base
43
+ # belongs_to :project
44
+ # belongs_to :task
45
+ # end
46
+ #
47
+ # In other words, the code above is the same as calling nested_belongs_to.
48
+ #
49
+ module ShallowHelpers
50
+ include BelongsToHelpers
51
+
52
+ private
53
+
54
+ # Evaluate the parent given. This is used to nest parents in the
55
+ # association chain.
56
+ #
57
+
58
+ # Maps parents_symbols to build association chain. In this case, it
59
+ # simply return the parent_symbols, however on polymorphic belongs to,
60
+ # it has some customization.
61
+ #
62
+ def symbols_for_association_chain #:nodoc:
63
+ parent_symbols = parents_symbols.dup
64
+ if parents_symbols.size > 1 && !params[:id]
65
+ inst_class_name = parent_symbols.pop
66
+ finder_method = resources_configuration[inst_class_name][:finder] || :find
67
+ instance = resources_configuration[inst_class_name][:parent_class].send(finder_method, params[resources_configuration[inst_class_name][:param]])
68
+ load_parents(instance, parent_symbols)
69
+ end
70
+ if params[:id]
71
+ finder_method = resources_configuration[:self][:finder] || :find
72
+ instance = self.resource_class.send(finder_method, params[:id])
73
+ load_parents(instance, parent_symbols)
74
+ end
75
+ parents_symbols
76
+ end
77
+
78
+ def load_parents(instance, parent_symbols)
79
+
80
+ parent_symbols.reverse.each do |parent|
81
+ instance = instance.send(parent)
82
+ params[resources_configuration[parent][:param]] = instance.to_param
83
+ end
84
+ end
85
+ end
86
+
87
+ end
@@ -32,6 +32,7 @@ module InheritedResources
32
32
  # all created when you inherit.
33
33
  #
34
34
  module UrlHelpers
35
+ protected
35
36
 
36
37
  # This method hard code url helpers in the class.
37
38
  #
@@ -39,7 +40,7 @@ module InheritedResources
39
40
  # is being processed (and even more cheaper when we are using nested
40
41
  # resources).
41
42
  #
42
- # When we are using polymorphic associations, those helpers rely on
43
+ # When we are using polymorphic associations, those helpers rely on
43
44
  # polymorphic_url Rails helper.
44
45
  #
45
46
  def create_resources_url_helpers!
@@ -75,6 +76,7 @@ module InheritedResources
75
76
  collection_ivars = resource_ivars.dup
76
77
  collection_segments = resource_segments.dup
77
78
 
79
+
78
80
  # Generate parent url before we add resource instances.
79
81
  unless parents_symbols.empty?
80
82
  generate_url_and_path_helpers nil, :parent, resource_segments, resource_ivars
@@ -120,15 +122,59 @@ module InheritedResources
120
122
  collection_ivars << '(@_resource_class_new ||= resource_class.new)'
121
123
  end
122
124
 
125
+ # If route is uncountable then add "_index" suffix to collection index route name
126
+ #
127
+ if !singleton && resource_config[:route_collection_name] == resource_config[:route_instance_name]
128
+ collection_segments << :index
129
+ end
130
+
123
131
  generate_url_and_path_helpers nil, :collection, collection_segments, collection_ivars
124
132
  generate_url_and_path_helpers :new, :resource, resource_segments, new_ivars || collection_ivars
125
133
  generate_url_and_path_helpers nil, :resource, resource_segments, resource_ivars
126
134
  generate_url_and_path_helpers :edit, :resource, resource_segments, resource_ivars
135
+
136
+ if resource_config[:custom_actions]
137
+ [*resource_config[:custom_actions][:resource]].each do | method |
138
+ generate_url_and_path_helpers method, :resource, resource_segments, resource_ivars
139
+ end
140
+ [*resource_config[:custom_actions][:collection]].each do | method |
141
+ generate_url_and_path_helpers method, :resources, collection_segments, collection_ivars
142
+ end
143
+ end
144
+ end
145
+
146
+ def handle_shallow_resource(prefix, name, segments, ivars) #:nodoc:
147
+ return segments, ivars unless self.resources_configuration[:self][:shallow]
148
+ case name
149
+ when :collection, :resources
150
+ segments = segments[-2..-1]
151
+ ivars = [ivars.last]
152
+ when :resource
153
+ if prefix == :new
154
+ segments = segments[-2..-1]
155
+ ivars = [ivars.last]
156
+ else
157
+ segments = [segments.last]
158
+ ivars = [ivars.last]
159
+ end
160
+ when :parent
161
+ segments = [segments.last]
162
+ ivars = [ivars.last]
163
+ end
164
+
165
+ segments ||= []
166
+
167
+ unless self.resources_configuration[:self][:route_prefix].blank?
168
+ segments.unshift self.resources_configuration[:self][:route_prefix]
169
+ end
170
+
171
+ return segments, ivars
127
172
  end
128
173
 
129
174
  def generate_url_and_path_helpers(prefix, name, resource_segments, resource_ivars) #:nodoc:
130
- ivars = resource_ivars.dup
175
+ resource_segments, resource_ivars = handle_shallow_resource(prefix, name, resource_segments, resource_ivars)
131
176
 
177
+ ivars = resource_ivars.dup
132
178
  singleton = self.resources_configuration[:self][:singleton]
133
179
  polymorphic = self.parents_symbols.include?(:polymorphic)
134
180
 
@@ -1,3 +1,3 @@
1
1
  module InheritedResources
2
- VERSION = '1.1.2'.freeze
2
+ VERSION = '1.2.0'.freeze
3
3
  end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/test_helper'
1
+ require File.expand_path('test_helper', File.dirname(__FILE__))
2
2
 
3
3
  class Student
4
4
  extend ActiveModel::Naming