I am using datamapper in a Sinatra application. I currently use the command
DataMapper.finalize.auto_upgrade!
to handle the migrations. I had two Classes (Artists and Events) with a 'has_n' and 'belongs_to' association. An Event 'belonged_to' one Artist and an Artist could have many Events associated with it.
I changed the association to be a many_to_many relationship by deleting the previous parts of the class definition which governed the original one_to_many association in the models and adding
has n, :artists, :through => Resource
to the Event class and the corresponding code to the Artist class. When I make a new Event, an error is kicked off.
#<DataObjects::IntegrityError: events.artist_id may not be NULL
The :artist_id field is a relic of the original association between the two classes. The new many_to_many association is accessed by event.artists[i] (where 'i' is just an integer index going from 0 to the number of associated artists -1). Apparently the original association method between the Artist and Event classes is still there? My guess is the solution to this is to not just use the auto_upgrade method built into datamapper but rather to write an explicit migration. If there is a way to handle this type of change to a database and still have the auto_upgrade method work, that would be great!
If you need more details about my models or anything please ask and I'll gladly add them.
In my experience, DataMapper's auto_upgrade does not work very well -- or, to say the least, it doesn't work the way I expect it to. If you want to add a new column to your model, it will do what it should; try to do anything more sophisticated to a column and it probably won't behave as you expect.
For example, if you create a property of type String, it will initially have a length of 50 characters. If you notice that 50 characters is not enough to hold your string, adding :length => 100 to the model won't be enough to make auto_upgrade change the column's width.
It seems you have stumbled upon another shortcoming, although one may argue that, in your case, maybe DataMapper's behavior isn't that bad (think of legacy databases). But the fact is that, when you changed the association, the Event's artist_id column wasn't removed, and then when you try to save an Event, you'll get an error because the database says it is a required field.
Notice that the error you are getting is not a validation error: DataMapper thinks everything looks ok, but gets an error from the database when trying to save the object.
Hope this helps!
Auto-upgrade is not a shortcoming at all! I think of auto-upgrade as a convenience feature of DataMapper. It's only intended purpose is to add columns for you as far as I know. So it is great for getting a project started quickly, and managing test and dev environments without having to write migrations, but not the best tool for making modifications to a mature, live project.
For that, DataMapper does have migrations! Use the dm-migrations gem. The shortcoming is that they are not very well documented... at all. I'm actually working on changing a current project of mine over to using migrations, and I hope to contribute some instructions to the dm-migrations github wiki. But if you aren't ready to switch to migrations, you can also just update columns manually using an SQL client, and then continue to use auto-upgrade for new columns. That's what I have been doing for 2 years on my project :)
Related
We have a very old application with multiple databases. Recently we decided to make a switch from MSSQL to PostgreSQL. During the process we decided to keep all the table and column names in lower case. This affects our code a lot. We want to go with minimal change approach.
Problems :
Table name changes - We thought of overriding getters and setters for the table_name in model to avoid changes at many places. Creating a module and then including that in all the models was one option. But our setter can't get control because of "ar-octopus" gem. It actually hijacks the setter for table_name. So this approach fails.
We tried mapping dynamic methods like "find_by_UserID" to "find_by_userid" by overriding "method_missing". But dynamic_matchers in activerecord has marked the method as private, so this also doesn't works.
The only option now seems to be refactor the whole code to suit the table and column name changes. But we have used queries, direct column accessing like #person["Name"] in views as well and use of many dynamic functions like mentioned in point 2. So even after refactoring the whole code and testing it completely we wouldn't be sure whether all the code has been updated properly or not.
We want refactoring to be last option. But, don't know any other better way.
After many years of C/C++, PHP, some Ruby and other languages on one hand an different projects with different frameworks on the other, I now want to learn Rails.
After working through (Getting started-) Guides, I think Rails is powerfull and fairly easy to learn. And I feel ready to start with a non Bookshop app.
But a friend warned me about Rails's 'convention over configuration' and the way it 'does things'. I cant see a 'problem' with that, but are there pitfalls?
And: Are there things Rails does very different than other framworks?
You're going to either get zero replies or a bunch of opinions. I would recommend googling for "rails is opinionated". Hopefully that will turn up more examples of what you might run into.
Is it a problem? No, not really. Can it be a problem? Yes, absolutely.
Integrating with legacy databases can be a PITA sometimes. Or if you have some insane desire to name all your primary keys something other than "id" that can be a problem.
Not so much a problem really, but you're fighting a lot of convention.
Really, other than legacy databases I can't think of anything off the top of my head bothers me about it's conventions.
What your friend says is only somehow true. Rails's naming conventions are powerfull and keep your brain free for other things.
But: If you think you have experience ... you are learning Rails -> you are back to school.
Rail's 'naming conventions' are not only conventions. They are Rails somehow. So if you break the convention, you are off road and soon in the middle of no where. I think that this part of Rails could be better pointed out in Guides.
Let me give an example: (you are tired of "books" and start wit a little app around 'Pubs')
You scaffold your Pup (intended typo)
You then put in some logic, put some work, then you realize your (oops) typo. Now dark clouds arise. Since you are experienced, you start correcting the typo ... PupsController -> PubsController, filename of PubsController (you are already off road) ...
You will end up at the database table 'pups' ... (middle of nowhere)
This happened because you think you are experienced. A beginner has built a new scaffold (without typo) or asked here on SO how to correct 'correctly')
An other example is to name thigs 'more nice'. After years and many projects you are probably one, that never uses "unspecified" names like 'user','role','guest','owner' for classes and so on. So you start to name them (nicley?): PubUser, PubOwner, ... Noboddy told you "DON'T".
You put all in a namespace (there are many people here saying "don't") with the nice name 'PubApp'
Although your files are well organized, you will end in tablenames: pub_app_pub_owners and so on, not to think about the name of assotiative Tables between them.
And later on you will type something like
link_to 'add' new_pub_app_pub_pub_guest_url
link_to 'add' new_pub_app_pub_owner_pub_url
This is probaly not what your intention was to make things 'clean'. And if you take a look at the 'beginers' link...
link_to 'add' new_pub_guest_url
What I do not want is to preferre one or the other.
I want to point out, that - since you are not experienced with Rails - you dont know where the things you are doing (off road) are leading you. With only a hard way to return.
Thats somehow a pitfall.
But next time you will know about that and make a compromise: 'Pubowner' and 'Guest' (and 'pa' as namespace (if you realy want to)
so
link_to 'add' new_pa_pubowner_guest_url
Is not so bad. But its hard to reverse things so think before ...
When writing application using any framework it may be necessary to write a lot of configuration code. However if we follow Rails Standard conventions then it is possible to avoid the excess configuration and in some cases no configuration at all. Thus, explicit configuration would be needed only in those cases where you can't follow the standard convention.
Following are the conventions provided by rails:
Naming conventions: Active Record uses some naming conventions to find out how the mapping between models and database tables should be created. Rails will pluralize your class names to find the respective database table. So, for a class Book, you should have a database table called books.
Example:
Database Table - Plural with underscores separating words (e.g., book_clubs).
Model Class - Singular with the first letter of each word capitalized (e.g., BookClub).
Schema Conventions: Active Record uses naming conventions for the columns in database tables, depending on the purpose of these columns.
Foreign keys - These fields should be named following the pattern singularized_table_name_id (e.g., item_id, order_id). These are the fields that Active Record will look for when you create associations between your models.
Primary keys - By default, Active Record will use an integer column named id as the table's primary key. When using migrations to create your tables, this column will be automatically created.
There is a point I never had before, that took me a nerve or two: Rails is caching a lot - even in development env. (for gods sake it does!)
Let me construct a scenario (no; not a construct, happend to me in a more complex variant)
After enough hours of work and you did all that was on the plan for the day, you close with a little cleanup. Check evering is still working - smile - and off
sleep
Back to the Computer you start all up and get a 'constant xy is not ...', so but, but why, overnight?
The answer is easy (if someone tells you at least once): Rail's caching does not (or better cant) check if a class / file / method is just removed, and not altered (sometimes ...)
So the (one to many) deleted file removed the Class, it contained from the world, but not from Rail's cache. Power off did the rest.
I had more subtile situations, that i solved with a rails server restart after i looked out of the window, if I am still on earth ...
What I try to point out is, there is no magic behind, but be warned, if you think you are smart enough to touch the framework code (why and how ever you want to do that). Big cache gets you back where you are, at beginners level.
In the app I'm working on, Courses have many Problems, which in turn have many Steps. Right now there is a form for adding Problems to Courses (and then Steps can be added to those problems). What we want is to have a form that just has a field for LaTeX input, and then process the TeX to create multiple problems with their steps.
At the moment, we're doing this all in the Problems controller. We have two methods, texnew which is identical to new except it has a different view that redirects to the other new method: texcreate, which uses helper methods to extract the problems and steps (using a series of regexes), tries to create them, and flashes somewhat informative error messages if something goes wrong.
The thing is, I keep on reading that we're really not supposed to be doing a bunch of stuff in the controller, and we should favor doing things in the model instead. Virtual attributes might be the right idea for taking in a text field and processing it to create a single problem, but I can't figure out how to make it work for multiple problems, or how to generate any sort of error messages if something goes wrong somewhere along the way.
Is there some better/more idiomatic way to do this?
You don't really need virtual attributes for this if all your relationships are setup properly. You can use the new rails3 nested attributes. There is a good article on them here. This will allow you to rely more on model validation logic and keep the lean controller fat model idiom that rails encourages.
I got a legacy database which have about 10 identical tables (only name differs).
Is it possible to be able to use the same business entity for all tables without having to create several classes/mapping files?
You can use the entity-name feature if you are using NHibernate v2.1 or higher. It is poorly documented but I am actively using the feature. It has gotten hard to find the documentation on it but look here:
Section 5.3 in
http://docs.jboss.org/hibernate/core/3.2/reference/en/html/mapping.html#mapping-entityname
A couple of things to be aware of. You must now use entity-name instead of class name to refer to the objects. In general it is not an entirely transparent change moving from class names to entity names.
Session actions now require two parameters, for example:
_session.Save("MyEntity", myobject)
The entity-name controls what table the data goes into.
Some HQL queries do not work right anymore, sometimes you must use Criteria instead.
If you need a set of sample code I may be able post some, but far too busy at the moment. I suggest you look at the limited info you can find and set it up for a very simple object and multiple tables to learn how it all works. It does work.
You can create a base class with all the properties, but you still need to map them all.
For that, you can either use copy&paste, XML entities (see examle at http://nhibernate.info/doc/nh/en/index.html#inheritance-tableperconcreate-polymorphism), or a code-based mapping method (Fluent or ConfORM). They usually make reuse easier.
One thing that bothers me about nHibernate is that it is not 100% compile time tested.
If I rename a column in my database table, and I update the mapping file for that table, when I hit compile I should get errors in all my query code (hql/criteria whatever) of where I referenced that column name and how it is spelled wrong.
The whole point (for me anyway) of using an ORM was that database changes won't break my queries.
In other words, I will be notified at compile time of what needs to be fixed, instead of getting runtime errors etc.
To achieve what you want I think your best solution is to use a combination of Fluent NHibernate and nhlambdaextensions. Fluent NHibernate will give you type-safe checking on your mapping files (so if you change a property on your entity, the compiler will throw an error if you don't also change the property on your mapping class). The lambda function extensions will give you type-safe queries via the Criteria API (not HQL since that's just magic-strings SQL-with-objects).
Also to clarify your question, you said:
If I change a column (rename) in my
database table, and I update the
mapping file for that table, when I
hit compile I should get errors in all
my query code (hql/criteria whatever)
of where I referenced that column name
and how it is spelled wrong.
Just changing the database side should break nothing (assuming you also make the change in your XML mapping file). Your code does not reference the column="first_name" portion of the mapping, it references the name="FirstName" portion. If you do not change your entity, renaming a column (from "firstname" to "first_name", for example) in the database will not break your queries as long as you update your mapping file as well.
You should look at Castle ActiveRecord. I used this before and it allows you to not worry about the mapping files (.hml) as much. It lets you make your changes at the class level definitions, and the mappings files were generally untouched.
If you are writing bad queries, that sounds like a design problem, not an nHibernate problem.
You won't get errors providing the Property names haven't changed, as most people use HQL for their queries in NHibernate.However if you do change the Property names and not the HQL you will indeed get broken queries, e.g.:
FROM User Where User.Surname = 'bob'
Change the Surname property to Lastname and it'll break. It's a feature lacking in NHibernate but would make a good project for the contrib - a Subsonic style query interface. This a project sort of similar but still use HQL.
As mentioned above ActiveRecord and Fluent NHibernate are the closest to type checking with NHibernate. Both enforce that you inherit your classes from their base class, as you'd expect and ActiveRecord is not intended for production use - Ayende has said in a video that's meant to be a prototyping tool for NHibernate.
Hibernate uses dynamic byte code generation to create the mapping classes, based on the mapping configurations.
The fundamental point of ORM is to enable auto-magical mapping (bridge) between Objects and Relational systems. Thus: ORM.
if you want to strongly type your objects rather than using xml config which can cause alot of runtime issues if not properly tested, I would look into FluentNHibernate which has convention maps that allow you to map your classes to data in code. Made my life alot easier especially when first starting with NHibernate wish i had found it before i knew how to properly map using xml
Does NHibernate have the equivalent of the Java version's schema validator? In which case, you could add a step to your build process to build the session factory and run the validator-- building the session factory should also compile named queries, hence validating them too.
Hmm, looks like it supports something like that: http://nhibernate.info/blog/2008/11/22/nhibernate-schemavalidator.html
NB this means your build process will fail to work if your dev database is not available--- which I would regard as a bad thing.