I am using Ruby on Rails 3.0.7 and I have multiple resources that almost have the same behavior. That is, those almost have same model, controller and view codes and same database table columns definition.
So I would like to find a way to DRY those resources. I already implemented modules and mixins for those in order to share part of the code (as validation methods, callbacks, view files but not controller files that, anyway, have very similar code).
Now, how can I do to handle this common behavior? Should I use something that Ruby on Rails developers named as acts_as_something? What do you advice about?
I think you already did that, just name a method in your modules act_as_your_module_name and make sure your module extends from your Base Class, e.g. ActiveRecord::Base.extend act_as_your_module_name
http://www.cowboycoded.com/tag/acts_as/
Related
This is my first on StackOverflow and I'm a new rails developer.
I'm using RoR to create an inventory application for Magic: The Gathering cards. I've found a Json API that I'd like to use to pull data on all of the cards, sets, etc into a local database.
My initial inclination is to create a helper class to manage all of this (which can also be called in seeds.rb during db:setup), but I have no idea where I should put this class in my project's directory structure. It's not really a model/controller/view, so I feel it should be kept separate from those parts of the app.
Further more, I'm having trouble testing any class I do make. I initially created a directory app/classes and put the class there. Then in my spec directory, I created spec/classes and created the spec file. Accessing my helper class from the spec did not work in the same way that accessing my models in their spec classes did.
I'm at a loss as to how to do this and quite a bit of googling and searching on here has just left me more confused. I'd love any help that can be offered. How would you do this?
You don't mention specifically what issues you're encountering, but for starters app/classes isn't in rails's autoload path - rails' require magic doesn't know to look in there to find these classes (as an aside, 'classes' sounds like a slightly meaningless name - models & controllers are classes too).
You can add to the paths rails searches (see config.autoload_paths in config/application.rb) but I would put this in lib (and the corresponding specs in spec/lib).
It would also work just fine with these classes in app/models, whether or not it does there is down to choice. There's nothing that says that files in there have to be active record subclasses - the decision of whether or not this functionality belongs in there boils down to whether it works/feels like a model to you.
In a Rails app I'm creating, I have some code in my controller that I'm wondering whether it's in the proper place. The code is fairly insignificant, it stores ids in an array to show 'recently viewed' pages. It's about 3 lines of code, but I'm thinking to the future, what if this feature expands? I don't want my controller to be bloated.
I could make a module, but in that case where should I store the file?
Is the controller the right place to be doing the session management?
Any suggestions on my code organization?
Thanks
If it is specific to the controller, keep it in the controller.
If it applies to all controllers, it goes in ApplicationController.
If it is shared by some controllers and not others, then inherit from a controller that inherits from ApplicationController, or use include/extend or make a module that extends ActiveSupport::Concern (which is what Rails uses internally fairly commonly).
And it's best to keep everything in app/controllers or some subdirectory, sub-subdirectory, etc. Rails autoloading depends on the path to match up with the module namespace, so A::B::C belongs in app/controllers/a/b/c.rb. Don't make it deep like Java, etc. Just have the number of directories/modules you need to keep it organized.
Note: though controllers aren't as problematic to have in their own modules, in my experience your models should stay in the root, like app/models, or you'll have problems.
I'd also avoid storing too much in session if you can help it. Store in the DB (or long-life cookies, if it is browser-environment specific) instead. For example- if someone logs out and they were looking at one record, they might want to log back in later and have a list containing the link to that record.
BTW- you weren't asking and probably already have the code for storing recently visited pages in session, but here are similar questions/answers:
https://stackoverflow.com/a/10813602/178651
http://www.rorexperts.com/how-to-store-visited-pages-in-session-object-in-rails-t941.html
should a non-database data class I need be created as a Rails 3 "model", or just a basic class in the /lib area?
I want to build some classes to represent calculated domain objects. So the fields won't reference a database table/column. There will be also methods in the class that with the data.
Question - When generating these classes should these be just normal Ruby classes I put in the /lib area? Or should I/can I use rails models for this (and generate with "rails g model ...")?
Tableless models should probably be kept in app/models. I have several that access APIs. ActiveModel in Rails can help bring in some of the useful functionality of Active Record.
Interesting question..I had the same question a few weeks back.
I put those class under model directory. This is how I came up with that decision. The class that I wrote was completely related to a particular app no common features to share with anyone at-least at the moment. I also needed to use some of my existing models to query some data in that class. So I made it a class under model directory. I might be wrong but thats what I have done now.
In another case where I am using a certain api sets for a web and mobile app I am thinking of making the code which interfaces with the api into a gem. The thing to note here is that the api set is also part my system and it will only be used by my apps.
Classes that don't map to database tables can still reside inside of app/models. Instead of extending your class from ActiveRecord::Base, you can simply declare your class without any extensions (or your own extensions).
The CanCan ability model is a good example of this. It resides in app/models/, however does not extend ActiveRecord::Base. More information on CanCan's ability model can be found here: https://github.com/ryanb/cancan/wiki/Defining-Abilities
Also consider that code under lib/ will not reload in the development environment by default.
I'm just starting out in Rails and there's a lot I still need to learn so I'm likely to be on Stackoverflow more often than normal asking beginner Rails / Ruby questions.
I'm just trying to figure out how Helpers work in Rails. From what I've seen so far, Helpers are intended to be used with Views and not so much with your Controllers.
However I would like to make a simple function that will validate the user input given in params (check if certain params are defined and optionally check if their value is valid).
Can anyone explain to me what would be the best way of implementing this? (Keeping in mind that I will want to use this in many different controllers so it should be globally available.)
I also noticed that by default Rails does not generate a lib folder in the main application folder. Are developers to place their libs outside the app folder in the main folder, or does Rails use libraries differently?
With regards to your validation issue, it depends on what you are validating.
If the data makes up objects from your problem domain, also known as models, then you should use the built in validators from ActiveModel. This is probably what you should do, but its hard to say without knowing the exact problem. See the Rails Guides on Validations. You can tell if this is the case by asking yourself if the data that needs validation will be stored after you get it. If so, its most definitely a model. An example of this kind of data would be the title and text fields of a blog post being sent to Rails from a browser form.
If the data is something tertiary to your models, or specific to presentation, then you should be fine using helpers. You noticed that helpers are used mostly in the views, and although this is true, theres nothing stopping you from using them in the controllers, you just have to declare that you will use them using the ActiveController#helper method. Inside the ApplicationController class, a lot of devs will put helper :all to just include all the helpers in all the controllers. Once the code has been required once, it doesn't really incur that big a performance hit.
Do note that almost all incoming data can be modeled using a model. A big school of thought in the Rails world subscribes to the Fat Model idea. People say that putting as much code as possible in the model and as little in the controller as possible separates concerns properly and leads to more maintainable code. This suggests that even if you don't think the incoming data is modelable (in the sense that you can create a model to represent it), you should try to make it a model and encapsulate the logic around validating it. However, you may find that making a helper function is faster, and either will work.
Your notion of validating user input is a good one. I get the feeling that as you are new to Rails you are used to doing these things yourself, but that doesn't quite apply here. In the Rails world, a lot of the common stuff like validations is handled by the framework. You don't have to check for presence in the params array, instead you call validates_presence_of on the model and let Rails spit the error out to the user. It makes things easier in the long run if you let the framework do what it is designed to.
With regards to your question about the lib folder, it doesn't really matter. You can put miscellaneous support files and libraries in the lib folder in the root directory and they will be available for use in your application (the files in the app folder). You can also choose to abstract your code into a plugin or a gem and include it that way, which a lot of people opt to do. My suggestion for this would be to read up on the notion of gems and plugins before diving in.
Want you want is probably a custom validator (in Rails3):
http://railscasts.com/episodes/211-validations-in-rails-3
You can either add libs in a lib folder you create, or add them to config/initializers in a file you add. Files in the initializers directory are automatically loaded by Rails.
I have a Rails application with several models-views-controllers which have some similar characteristics, for example 5 different models can be commented on, voted on or tagged, I am also heavily using external plugins.
At the moment I introduced comments, votes, tags, etc. only to a single model (and its view and controller). However, now that I am happy with the results, I want to cut out this common functionality from the particular MVC of one model and allow access to it from all other models.
Some questions before I start doing this (and maybe some general advice will also be great):
1 - How should I go about it? I was thinking creating a module in "lib" directory (is it the same as mixin class?) and then moving reusable view code to common partials. What about the controller code?
2 - As I was just learning Ruby on Rails during the coding of the first model, I went with a probably incorrect way of adding a bunch of methods to the controller. I have a method that adds a comment (addcomment), adds a vote (addvote), etc. All these methods require non-standard (non-RESTful) routing via :collection. From what I understand, the correct way would be to move comments controller functionality to its own controller and access via standard RESTful routes. Is this what I should be doing?
3 - Many plugins (eg. act_as_commentable) do not explicitly require loading a Module, just a line "act_as_commentable" somewhere in the Model. Can I use something like this for my common functionality? How does it work?
A simple way is to split the code into modules and use mixin.
A better way is to write your own plugins for your common code.. like act_as_commentable
you can learn about it here: http://guides.rubyonrails.org/plugins.html
The correct way is to do a comments controller, and have it nested to your models, giving a restful routes like this: /mymodelname/1/comments.
An easy way to make such controllers is by using inherited_resources plugin.
scroll down to the "Polymorphic belongs to" section- there is a comments controller example
For repeated model code, put it in a module in the lib directory.
For controller code, put your duplicate code in ApplicationController.
For your view code, use partials.