Creating a rails view + controller that doesn't require a model? - ruby-on-rails

I'm a bit confused on what's the proper design for a page that doesn't actually need a model.
As an example, I want to create an export page that will allow a user to export various other models into a CSV.
Obviously I'll need a view (most likely a show.html.erb file), and a controller; but making some sort of Export model wouldn't really make sense, and the same goes for creating new/edit/index views.
Is the correct way to do this just to manually create the view + controller for my Export page? It seems weird to not have a model after reading the rails documentation which is so heavily based on the MVC pattern, but I also don't see any reason why I "need" to follow the pattern for a case like this where a model just wouldn't make sense.

My guess is you are using the scaffold generator which does create a model.
If you just want to create the views and the controller type this in your terminal.
rails g controller exports
This will create the views, controller, and assets associated with the controller. Just add the routes.
resources :exports

You don't need a model. The generators and assumptions in rails generally work better if you have a model, but you don't need one. You can manually create the controllers and views, or use rails g controller exports.
You might look into form objects to provide a model in the controller - those are plain old ruby objects that provide a model without a database record.
A form object for an Export might start like this:
class Export
include ActiveModel::Model
include ActiveModel::Validations::Callbacks
end

Create a controller in your app/controllers folder like this:
class ExportsController < ApplicationController
def show
#export = ... # Your query here
end
end
Create a folder named exports inside app/views folder.
Create your show.html.erb inside the exports folder you just created.
Add manually your exports#show route like this:
resources "exports", only: [:show]
You should be good to go. Add the necessary auth and before_action methods in your ExportsController.

Related

How to write and include regular ruby classes in rails

I'm learning Rails. I have a controller responsible for presenting data from parsing files uploaded by the user. I don't want the data to be stored anywhere in the model. Can I include a class that I can instantiate in my controller method? Here is a basic code example of what I mean:
This controller only contains one method:
class MyController < ApplicationController
def index
test = FileProcessorService.new
#test = test.test()
end
end
Here is the class that will handle the logic when the instantiates calls its method:
class FileProcessorService
def test
return 'This is a test'
end
end
My questions:
Where is the best place to store this class? How can I refer to this class in my controller?
Any advice on this particular topic of using classes in rails? Are instances of a regular ruby class a problem in the controller? I dont want my users seeing the same data. Thats why I dont want to include global variables in my controller. No models since I have an MVC back ground with Java MVC. I'll move on to models once I understand how the basic rails controller functionality works.
Thank you in advance for your help.
I usually put these in app/classes, or if there are a lot of them, into more specific folders likes app/services, app/notifiers, etc.
You can enable autoloading in config/application.rb:
config.autoload_paths += %W(#{config.root}/app/classes #{config.root}/app/services)
If they're not application-specific, extract them to a gem.

RoR: defining a class inside a view helper file

I have a view helper file, app/helpers/analysis_helper.rb, whose toplevel methods I've been using in various view files. Works fine. I then defined an AnalysisSummary class inside analysis_helper.rb to package up some view-specific functionality.
However, when I try to instantiate an AnalysisSummary in a view file, I get the error:
uninitialized constant ActionView::CompiledTemplates::AnalysisSummary
Perhaps Rails is telling me that I shouldn't be defining a class inside a helper file? If so, where would you suggest parking AnalysisSummary? It's not a controller, it's not a model...
Thanks.
In Railscasts #213 (Revised) (subscribers only link, alas), Ryan Bates provides an example of how (and why) you might include a class within a helper. The basic gist is this:
# app/helpers/calendar_helper.rb
module CalendarHelper
def calendar(date = Date.today)
Calendar.new(self, date).render
end
class Calendar
def render
# Calendar, render thyself
end
# ... additional methods called by #render
end
end
To those opposed to classes within helpers, what do you make of Ryan's choice? Helpers are for generating markup, right? So if all the logic within class pertains to rendering (rather complicated) HTML, I would think that what it does (as opposed to what it is) makes it appropriate for inclusion in a helper.
Why does it need to be a class? Why not just a collection of methods? That's what a helper is: a collection of helpful methods. Business logic does not belong in helpers. You can place your code in a module within the helper file if you want to give some more structure and organization, though.
You can put classes in app/models without it having to be an ActiveRecord class, but you should seriously consider what the purpose of your class is before you place it there.
If it concerns only rendering the view, and not accessing data directly, it belongs in the view or a view helper.
You can call the class by explicitely mentioning the helper name
ApplicationHelper::AnalysisSummary.new
But I dont think it is a good idea to have classes in helpers.
It's a module then :) Definately do not define classes inside helpers. Jsut use a simple module to do the job.

Where to place Rails code that is not a model, view, controller or helper?

I want to share code not related to views between several controllers in my Rails app. Where in the directory structure should I place it?
EDIT: the code in question if something all controllers use to determine how they render the model data
If the code is something like modules with utility methods in these could be placed in the lib folder. Alternatively you could create a common superclass for some of your controllers if they share behaviour.
Please post an example of the kind of code you're thinking of.
If it's "something all controllers use", I would place it in a base class used by all controllers. The "application_controller" comes to mind.
I have been creating modules in lib to provide code to other classes.
Here's an abbreviated example module that defines values for views that can be instantiated from different controllers.
module ControllerDefaultValues
def default_value_for_some_controller()
#controller_name = "some_controller"
end
end
To use this, simply include the module in your class:
class SearchesController
include ControllerDefaultValues
#
def search_some_controller
default_value_for_some_controller()
render(:partial => "search_results")
end
end
The main benefit of this method is it keeps your controllers directory focused on controllers and your models directory focused on logic.
Personally i think its fine, if its controller related to put it in your controllers directory, but, labelled as a module, e.g. app/controllers/what_am_i_module.rb since it is specific to the application in development.
lib seems like a place to me where i would put framework/core enhancements or monkey patches which are non-specific to the application.
If it's going to be used by all your controllers, stick it in application_controller.rb
All your controllers inherit from ApplicationController, so they'll all have access to them.

Programmatically Create Controller in Rails

What's the best way to dynamically create a controller in Rails.
I've got a class that needs to generate a bunch of controller that inherit from it. I could just create a bunch of files in /app/controllers, but they'd all be basically empty files. There's got to be a way to generate these classes dynamically and have them treated like other controllers in Rails, e.g. reloaded correctly in dev mode.
I tried putting this in a config/initializer:
FL.contact_types.each do |contact_type|
controller_name = "#{contact_type.pluralize}Controller"
Object.const_set(controller_name.to_sym, Class.new(ContactsController)) unless Object.const_defined?(controller_name.to_sym)
end
This worked, but I run into the dependency/reload problem and get “A copy of AuthenticatedSystem has been removed from the module tree but is still active” since the ContactsController inherits from ApplicationController which includes AuthenticatedSystem.
Is creating a bunch of empty files really the best solution?
Are you sure you need multiple controllers? Can you have a single controller that gets passed a value to indicate how it behaves? You could also make a module that has the common functionality in it, and have the empty controller files that only reference the module.
a route could be used to pass the type in:
map.route "/:type_of_contact/:action/:id/, {:controller => :contact_type}
now in all the actions, in params you have the key :type_of_contact to guide your system.
You'll want to make sure this is near the end of your routes so it doesn't override access to your other controllers.

Singular or plural controller and helper names in Rails

Is there any disadvantage to using singular names for controllers and helpers? Nothing seems to rely on this. It even seems helpers don't have to make the same choice about singular vs. plural as their corresponding controllers, at least according to my limited experimentation. Is that true?
Definitely plural.
With restful routing and a singular controller
Controller:
dog_controller.rb
Routes:
map.resources :dogs # => blows up
map.resources :dog # is ok, but...
dogs_path # => blows up
dog_path # => ok
Using a plural controller
Controller:
dogs_controller.rb
Routes:
map.resources :dogs
dogs_path # => ok
dog_path # => ok
rails generate controller --help has plural examples:
Example:
`rails generate controller CreditCards open debit credit close`
CreditCards controller with URLs like /credit_cards/debit.
Controller: app/controllers/credit_cards_controller.rb
Test: test/controllers/credit_cards_controller_test.rb
Views: app/views/credit_cards/debit.html.erb [...]
Helper: app/helpers/credit_cards_helper.rb
Using plural names for controllers is just a convention.
Plural names usually sound more natural (especially for controllers that are tied directly to a specific model: User -> Users, etc.), but you can use whatever you want.
As for helpers, all helpers are available for all controllers by default, so technically, how you name your helpers doesn't matter at all. It's just another convention to keep a controller's helper functions in a helper with the same name as the controller.
A Model is singular because it references a single object like User. A controller is plural because it is the controls (methods) for the collection of Users. How one names the routes is all up to that individual developer. I've never had a user complain that a URL for a web request is singular or plural. The end result to maintain a common convention for current and future contributors while serving quality page displays or the API requests for the end users.
You have a very complete explanation in the Rails guides:
http://edgeguides.rubyonrails.org/routing.html#resource-routing-the-rails-default
It is the Rails convention that one controller handles one model, whether one or more instances of that model can exist during runtime. However, you can have a Rails application where (some of) the controllers (and the associated views) are not associated with any particular model, but rather handle a more complex set of functionality. In this case, the automatic pluralization doesn't make any sense.
The Rails application I'm currently working on fits into this category, and it's simply an irritation to me that Rails expects that the identifiers I define as a singular in one place are then used in their plural forms in other places. For example, I might want to define something like this in config/routes.rb:
resource :dashboard, :only => [:show]
and then I want a controller DashboardController to display summary information about certain aspects of the application, gathering information from more than one database table. So here, Dashboard does not refer to any model of the application, and it would be just weird to have the controller's name be DashboardsController.
I found a good solution to the irritation of automatic pluralization in this answer. In short, edit file config/initializers/inflections.rb and add the words you don't want to be automatically pluralized to this definition:
ActiveSupport::Inflector.inflections do |inflect|
inflect.uncountable %w( dashboard foo bar baz )
end
If the controller is a resource then it must be plural...
For example
Controller
articles_controller.rb
Model
article.rb
But you can use singular controller names when you do not have corresponding models like
welcome_controller.rb
The naming convention of controllers in Rails favors pluralization of the last word in the controller's name, although it is not strictly required (e.g. ApplicationController).
For example, ClientsController is preferable to ClientController, SiteAdminsController is preferable to SiteAdminController or SitesAdminsController, and so on.
Following this convention will allow you to use the default route generators (e.g. resources, etc) without needing to qualify each :path or :controller, and will keep URL and path helpers' usage consistent throughout your application.
Ref: Controller Naming Convention-Rails Doc
I feel better when I use singular for Controller name
Using plurals just sounds better, and then if you have a controller that handles a singular resourse, ie user, then you can still name the url /user.
With helpers there is often no need to have a helper for every controller, and often there will be helper methods you can use ascorss multiple controllers and rather litter them all through your application helper you could put them in custom helpers instead like eg layout_helper or any other well named file.

Resources