0

In my app, I want to define a class that my models, views, and controllers will use. My thought was to create a new directory, app/lib, where I created:

# /app/lib/donut.rb
class Donut
  def initialize(sprinkle_color)
    @sprinkle_color = sprinkle_color
  end

  def sprinkle_color
    @sprinkle_color
  end
end

Now this works fine, but when I added test/lib, those tests are not run when I run rake test. I can run rake test:all which works, but feels wrong.

Is this the right place to put classes, and if so, how do I get them into the tests?

P.S. Why do I need a class like this? Of course, I'm not actually creating a donut. It's a class that takes a set of arguments, and there are many models that have these objects. In addition, there are views that take these objects, or arrays of these objects. Finally, there is code that operates on lists of these objects, and I've put that code as static methods into this class.

Robert Martin
  • 15,401
  • 15
  • 52
  • 84
  • I'd probably put it in the `lib` directory (not within `app`). This question might provide more insight: http://stackoverflow.com/questions/15260984/guidelines-for-where-to-put-classes-in-rails-apps-that-dont-fit-anywhere – Jon Jul 06 '14 at 23:47

2 Answers2

2

Create folders in the app directory for custom classes but try to make them as descriptive as possible. For e.g. I use custom classes to properly format json for ajax loading datatables so the classes are located in app>datatables e.g.

app>datatables>users_datatable.rb

class UsersDatatable
  [...]
end

And so in the controller action I can render a view using the custom class:

respond_to  do |format|
  format.html
  format.json { render json: UsersDatatable.new(view_context, current_user) }
end

Here's another example that uses prawn pdf to generate a pdf view:

app>pdfs>quote_pdf.rb

class QuotePdf < Prawn::Document
  [...]
end

And in the controller action:

respond_to do |format|
  format.html
  format.pdf do
    pdf = QuotePdf.new(view_context, @quote)
    send_data pdf.render, filename: "quote_#{@quote.number}.pdf",
                          type: "application/pdf",
                          disposition: "inline"
  end
end

Testing is easy. Since the custom classes I use are associated with other resources I include the tests with them. i.e. with users, quotes. However you can create separate tests as you have done to be able to run them separately.

I would argue that app>lib is not descriptive enough. If possible, try and be more specific on what the custom class is actually doing.

Nicholas.V
  • 1,852
  • 1
  • 14
  • 30
  • Thanks, I'll rename `lib` to something more descriptive. But say I rename it to `confection`: how would I get `test/confection/donut_test.rb` to run as part of `rake test`? – Robert Martin Jul 07 '14 at 00:31
  • I'm not sure specifically for `rake test` as I use rspec. But I would say that `test/confection/` is not automatically included. Therefore you would have to specify in your `test_helper.rb` file to require the new directory. e.g. `Dir[Rails.root.join("test/confection/**/*.rb")].each { |f| require f }` I know this works for rspec. – Nicholas.V Jul 07 '14 at 00:58
0

I really dislike using "lib". Instead, I recommend using common design patterns as top-level directory names for other POROs. I do this in Rails projects. So that I have something like this:

app/
  models/
  controllers/
  services/
  policies/
  validators/

Etc.

A great showcase of this can be found here: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

Alex Moore-Niemi
  • 1,628
  • 1
  • 19
  • 20