I'm working on a Ruby on Rails project where I have a "post" model and a "reply" model. As their names suggest, they share a lot of common properties such as "author_id" and "body", but have their own respective unique properties as well (e.g. a post has a title, whereas a reply does not).
My question is: what's the best practice for dealing with two similar data models? Right now I'm treating them as two distinct models types, but as I start coding, I realized there will be a lot of repetition of code.
Take a look at mixins and modules in Ruby.
I think the best option here would be for you to create a module with the shared methods for each one and include it in both of them. That way, you don't have to repeat code and both will be independent.
You can check out the link provided by sczizzo above.
Related
I am trying to build a rack based web framework on my own, and I got confused when I refer to the design of Ruby on Rails.
Why should a model in Rails be named like User instead of UserModel, just like controllers or helpers do (for example, UsersController, PostsHelper)?
If the naming convention of model is good, why don't controllers or helpers follow the same rule? The Controller and Helper postfix is bit tedious.
Is it good to namespace everything? For example: Controller::User, Model::User.
Are there any good references/books that talk about the file structure of software?
I think that it's for convenience sake. You would never usually reference the controller and helper classes in your code, whereas you reference the model classes all the time. If they were called UserModel you would have lots of code like
user = UserModel.first
post = PostModel.first
there's two problems with this: it breaks the convention of "call the instance variable name after the class", which is a simple bit of self-documentation, and also it's cumbersome, with a lot of repeated information.
Also, the User class does actually represent a real life user, whereas the UserController class doesn't represent anything in the real world, it's just part of the machinery, if you know what i mean.
EDIT - some more answers:
3) Namespacing should be reserved for separate sections of your site, like an admin section for example: you can have an AdminController which inherits from ApplicationController, and adds a few more bits (such as a before_filter for checking that there's a current user and that they're an admin), and then your other admin section controllers inherit from AdminController. Namespacing in the routes allows easy management of this.
4) As for books, read this: http://www.amazon.co.uk/Agile-Development-Rails-Pragmatic-Programmers/dp/1937785564/ref=sr_1_1?s=books&ie=UTF8&qid=1450792683&sr=1-1&keywords=hansson+rails
It has some "Make a blog in 15 minutes" type stuff which i would actually recommend NOT diving into until you understand the underlying architecture, which is well explained elsewhere in the book. Too many people dive into Rails without understanding what they are doing (their hapless questions fill this forum).
All about convention, had to decide something. I don't speak for the rails core but this is my thoughts.
Model - In most cases, this is a representation of a single instance of a something. Although you could have cases where that is not the case, it helps me to conceptualize the singularity of it. I.e. I am dealing with a User not a Users
Controller/Helpers/Tablenames - In the database a User model maps to users table. The controller and helpers follow the same convention of the database.
Namespacing is good, but you should only namespace something when it makes sense on a clear boundary.
A great ruby framework to look at that is not rails is Lotus - http://lotusrb.org/ It uses namespaces heavily.
I have a model and table that I believe is perfectly suited to STI. My table is called Finances and has two types: Income and Expenses. Besides type there are three other columns: description, amount, and date.
I'm getting very nervous using STI in Rails, since it requires some hacking. I'm too new to Rails to hack up the code. Even though it works, I don't understand it. That seems dangerous.
My question is, how do I set up my model, controller, and view if I do NOT use STI? Any best practices to group items in my model? Or do I just do Finances.where("type = 'Income'") before setting up a view?
Edit: I made a gist to show the code I'm working with. When I run it I get the error:
undefined method `incomes_path' for #<#<Class:0x007fbc95f60b40>:0x007fbc93883220>
First, using STI is standard for Rails, so there is no need to feel nervous. And no need for "hacking".
It has been used very successfully by many developers. And as you have seen, you can find tutorials and general information on the net.
If on the other hand, you decide NOT to use STI, you have the choice of using
(a) completely separate models with their own tables, which will result in a lot of duplicated code, or
(b) create you custom "STI-like" behaviour by hand.
The second option could at least be interesting to learn more about Rails.
For example, in your Finances model you would define a scope incomes, like
scope :incomes, where(:type => 'Income')
then you can do Finances.incomes.
Then, if you have methods that apply only to one of the types, you should check that all records effectively are of the needed type.
Personally, I would advice you to use STI. You get a lot of functionality for free and you are doing it the Rails way.
Imagine, for example, other developers reading your code, they will ask themselves, why you didn't use STI, blame it on ignorance and - if need be - refactor it using STI.
STI is best if you are using inheritance structure like this. You don't really need to use Finances.where("type = 'Income'"). You can simply use Income.all. see these posts if they help you.
http://www.therailworld.com/posts/18-Single-Table-Inheritance-with-Rails
http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/
I am only looking for answers from senior/more experienced Ruby/Rails developers on this one, since I think this is a bit more advanced of a question.
I have a gem I am working on that adds some behavior to AR models. I have to test it for a lot of different associations (has_many, habtm, has_one etc), and I also have to test behavior when different options are passed for the associations (e.g. :foreign_key). Now with all these different models, I can use the same table in the database because the fields themselves do not need to change, only behavior specified through has_many, belongs_to and so on.
Keep in mind there are a lot of different options, so the number of models is quite large.
First I don't think it would be bad practice to have the definition of the models next to / in the test itself, for readability purposes (if I have multiple tests that use the same model then I would group them together and use the before method). So this is one of my goals, and you can comment on this if you don't agree.
The second thing I am not sure of is I wanted to keep the simple/same name of the model in all the tests, for example "Task", instead of TaskWithManySubtasksAndForeignKey or something ugly like that. The problem is there are so many models it's hard to come up with meaningful and simple names. I'm not quite sure about this - using the same name, since it's a constant, is a little problematic. I have a solution with a proxy class but I don't think this is the optimal solution. I was considering using variables (with the let method) like "taskModel", but it seemed a little verbose and unusual.
One other option that comes to mind, but I am not sure is possible to do easily, is to remove an existing association and then define a new one. So e.g. add a has_many and then remove it, add a habtm...
How would you go about doing this?
Defining unique models in the spec files is not necessarily a bad idea since it makes it easy to see exactly how each model is defined. The obvious problem with this approach is if you want to reuse the models in other test files. The Rails approach to this is to define all the models in separate files and then just require them in the tests that need it.
I think it really just depends on how many models you have and how much you want to reuse. In one of my gems, I took the approach of defining the models in the spec file, in another gem, I defined them in the spec helper, and in yet another I took the Rails approach and used a separate directory for them. If you asked me which one I preferred, I'd probably go with the spec that also contains the models because it's all in one place. Definitely a subjective problem though.
Another approach I've taken on occasion is to create an anonymous class that's guaranteed to only be around for the life of that test:
describe 'my test' do
let(:my_class) do
Class.new(Task) do
has_many :things
belongs_to :something_else
end
end
it 'should have many things' do
my_class.should have(100).things
end
end
I'm writing a rails application, and I need to name one of my model Test, and inside that model I need a class attribute - when I tried the first one, I couldn't run any UT since Test ceased to be a module - so I ranamed to Tst. I haven't even tried naming a column class - I went with clss. What names would you use? Are there any conventions known amongs RoR developers for such situations?
I haven't seen anything for Test but class is typically changed to klass.
Ruby and Rails have several different objects part of a standard library. You cannot use an already-defined class name as name for your models.
For instance, you cannot call a model Thread or Object because Ruby already defines a Thread and Object class.
Likewise, Ruby has a library called Test::Unit so you can't use the Test namespace as model name.
There isn't a real full list of reserve objects because it really depends on your current environment. However, in order to successfully use Rails, you should at least have a basic Ruby knowledge so that you know the names of the most common standard library classes.
I've run up against this a few times (writing educational software--where you regularly want to model 'tests' :-). Depends exactly what you're modeling I suppose, but I usually opt for 'quiz' to avoid conflicts. I'd love to hear a better solution, since I find 'quizzes' an awkward plural.
Like dj2 said, class is usually done as 'klass'.
Please check the following page for Rails reserved words: http://reservedwords.herokuapp.com/ None of these words should be used as class or attribute name.
I have a rails app moving along fairly well, but the fact that I'm doing this myself means that some poor sod is eventually going to see this and say, "What the hell were you thinking? Why did you put this here?!?!"
Where is that poor, sorry soul going to expect to see a series of classes that aren't used by anything but a single model class? Obviously, I could chuck it in the_model.rb along with class TheModel, but this may expand beyond the planned two classes...
I thought about lib, but it doesn't need to clutter everyone's view of the world....
Thank you.
My predecessor thanks you.
Leave them in the_model.rb until you need them in more than one place. If you refactor needlessly, you're not doing the simplest thing that could possibly work. You aren't gonna need it.
At that point, the general pattern is to create a directory for "concerns". See this weblog post by Jamis Buck or this one by Peter Marklund for more information.
In general: follow the Rails naming conventions when translating class names into filesystem locations. (that is: keep the class FooHelper::Bar in foo_helper/bar.rb)
You can make exceptions for small helper classes that are only used once and keep them in the same file as your model, but those should be exceptions. (but the converse is also true, don't create one-line thousands of single line files)
Use modules and class namespaces to your advantage. If you have a helper class that is only used by (and dependent on) your model, put them into the namespace of the model class:
class TheModel::HelperClass
end
the location in the file system would be app/models/the_model/helper_class.rb
And something that is not dependent on your model can probably still be namespaced
module Bar
class Foo
end
end
living in bar/foo.rb, of course
You should probably not be afraid to put things that are not models into lib -- that's what this directory is for
I'd say concerns, while useful, are not really the right way to go because that is a way to split a single class into multiple files and you don't seem to be doing that.