Sinatra with existing database (that doesn't abide by naming conventions) - ruby

I have an existing legacy Firebird database with nonstandard table and field names.
I would like to write a Sinatra app that can access it and display information. I've seen stuff like dm-is-reflective that appears to work when a database has proper naming conventions, but how do I use DataMapper (or ActiveRecord whichever is the easiest) to access those tables?
For example, assuming I had these two tables:
Bookshelfs
shelf_id: integer
level: integer
created: timestamp
Book
id: integer
id_of_shelf: integer
title: string
pages: integer
Something like with odd naming conventions that don't follow any set pattern and where one table's record might "own" multiple entries in another table even though there is not foreign_key assigned.
How would you set up datamapper (or activerecord) to communicate with it?

Look in to this gem to get setup with ActiveRecord on Sinatra:
https://github.com/bmizerany/sinatra-activerecord
As for how to define the relations, activerecord can do this easily.
class Book < ActiveRecord::Base
belongs_to :bookshelf, :class_name => 'Bookshelf', :foreign_key => 'id_of_shelf'
end
class Bookshelf < ActiveRecord::Base
has_many :books, :class_name => 'Book', :foreign_key => 'id_of_shelf'
end

Assuming you figured out how to connect to your legacy database using ActiveRecord's Firebird adapter, the next thing I would do is define a view on top of each table, e.g.
CREATE VIEW books AS SELECT * FROM Book;
CREATE VIEW bookshelves AS SELECT * FROM Bookshelfs;
This way you can simply define models Book and Bookshelf in ActiveRecord as per usual and it will find everything in the right place inside the database.

Related

Possible to use `one_to_many_through` associations in Sequel ORM?

I have a case where one model is related 2 other ones. I am trying to correctly setup the model relationships between these 3 models.
A simplified example... The first 2 tables are clients and invoices:
db.create_table(:clients) do
primary_key :id
String :name
end
db.create_table(:invoices) do
primary_key :id
String :description
Integer :balance
end
A third table, called files, contains records for files which can be related to either clients or invoices:
db.create_table(:files) do
primary_key :id
String :name
String :path
String :type # [image, pdf, word, excel]
end
There are 2 joiner tables to connect files to clients and invoices:
db.create_table(:clients_files) do
Integer :client_id
Integer :file_id
end
db.create_table(:files_invoices) do
Integer :invoice_id
Integer :file_id
end
The question is, how to correctly set up the relationships in the models, such that each client and invoice can have one or more related files?
I can accomplish this using many_to_many and has_many :through associations, however, this doesn't seem to be the right approach, because a given file can belong to only one customer or invoice, not to many.
I can also do this using polymorphism, but the documentation discourages this approach:
Sequel discourages the use of polymorphic associations, which is the
reason they are not supported by default. All polymorphic associations
can be made non-polymorphic by using additional tables and/or columns
instead of having a column containing the associated class name as a
string.
Polymorphic associations break referential integrity and are
significantly more complex than non-polymorphic associations, so their
use is not recommended unless you are stuck with an existing design
that uses them.
The more correct association would be one_to_many_through or many_to_one_through, but I can't find the right way to do this. Is there a vanilla Sequel way to achieve this, or is there a model plugin that provides this functionality?
With your current schema, you just want to use a many_to_many association to files:
Client.many_to_many :files
Invoice.many_to_many :files
To make sure each file can only have a single client/invoice, you can make file_id the primary key of clients_files and files_invoices (a plain unique constraint/index would also work). Then you can use one_through_one:
File.one_through_one :client
File.one_through_one :invoice
Note that this still allows a File to be associated to both a client and an invoice. If you want to prevent that, you need to change your schema. You could move the client_id and invoice_id foreign keys to the files table (or use a single join table with both keys), and have a check constraint that checks that only one of them is set.
Note that the main reason to avoid polymorphic keys (in addition to complexity), is that it allows the database to enforce referential integrity. With your current join tables, you aren't creating foreign keys, just integer fields, so you aren't enforcing referential integrity.

How do I declare model attributes in Rails 4?

I am trying to understand Rails and I dont understand how I declare the model attributes correctly. For now my user class is looking like this:
class User < ActiveRecord::Base
has_many :users # Friends
end
By Googling I have understand that before Rails 4 one could determine attributes with the attr_accessible, like this:
attr_accessible :firstname, :lastname, :age, :sex
But this seems to be deprecated, how can I do that same thing in Rails 4?
has_many :users is not a model attribute, its model association. It means model User can have many User objects, which is incorrect. (also does not make sense even literally)
What attr_accessible does?
Specifies a white list of model attributes that can be set via
mass-assignmen
To add attributes to a model, you need to generate migrations.
Example, lets add name attribute to users:
rails generate migration AddNameToUsers #creates a migration file to add `name` column to `users` table
followed by:
rake db:migrate # executes migration file creating `name` column in `users` table
Now you can access these attributes simply as:
user = User.new
user.name
Again, if you want to mass-assign this attribute at some point of your code, you will need to specify this in your class with attr_accessible, as in your original example.

Datamapper - create unique index over belongs_to attribute

I'm using DataMapper connected to an SQLite backend. I need to create a Unique index across my four belongs_to columns. Here is the table.
class Score
include DataMapper::Resource
property :id, Serial
property :score, Integer
belongs_to :pageant
belongs_to :candidate
belongs_to :category
belongs_to :judge
#What we want is a UNIQUE INDEX on the four properties!
end
Things I've done:
A unique index on the four via something like :unique_index => :single_score. This works only if you have a property already included.
validates_uniqueness_of, I think the scope only works for a 2-column unique index.
My current solution, which is to just create a dummy field "dont_mind_me", just so I can put :unique_index => single_score in it and everything works. Is this something that's okay to do?
Create an index using raw SQL, SQLite supports a unique index among the four fields.
Basically there are two parts of this question:
Is my solution okay, or should I find another one? I'm at wit's end dealing with what seems to be something trivial, even with raw SQL
How do I create an "after_create_table" hook in DataMapper? The hooks in the documentation only tell about post-CRUD data.

How do you add new column named "unique_id", and associate mailboxer with devise?

I have a question.
Devise and mailboxer are installed on my rails app without any problem.
The problem is that you have to use "email" or "name" column to associate devise user with mailboxer.
Obviously, devise doesn't have column called "name" in Users table.
So, if you use "email" then users will see everyone else's email address that they wanna hide.
I want twitter-like registration.
They've got unique_id(account name) that never will be changed.
To achieve that, How can I?
I. Add column named "unique_id" to Users table?
command: rails g migration AddUniqueIdToUsers unique_id:string
to create migration file, and open and edit like this
class AddUniqueIdToUsers < ActiveRecord::Migration
def change
add_column :users, :unique_id, :string
add_index :users, :unique_id, :unique => true
end
end
II. How do I associate devise with mailboxer by using "unique_id" column?
Thanks in advance
Obviously, devise doesn't have column called "name" in Users table
That is entirely up to you, all Devise does (or wants you to do) is add a few records that tell it how to function. If you look at the devise generator you can see that all it does is add a couple of columns to your migration.
I want twitter-like registration. They've got unique_id(account name) that never will be changed. To achieve that, How can I?
First of all, a unique ID is always given to you by free in Rails (indeed, in most typical web applications using a database backend, each row has a unique ID).
But, if you also want users to select a username, and have that be unique as well, the you could do as the mailboxer readme states and simply override the usage of name with your own database column like username, like so:
Mailboxer.setup do |config|
# ...
#Configures the methods needed by mailboxer
config.email_method = :email
config.name_method = :username
# ...
end
Or, if you want to stay out of the mailboxer config file, you can simply use alias_method and do this (given that you have a username column):
class User < ActiveRecord::Base
alias_method :name, :username
acts_as_messageable
end
Either way, Devise doesn't restrict you in which columns you use on your User model, and it seems that Mailboxer also doesn't restrict you in which columns you use to attach the gem to the User model. So you have as much flexibility as you want without having to built this functionality by yourself.

How to set up two models having a has_many association with each other

I'm looking for a suggestion on how to set up two models, Teacher and Subject. A Teacher can have many Subjects, and a Subject can have many Teachers. Another thing to consider in the relationship between the two models is that a Teacher can create a Subject and add other Teachers to the Subject.
I think I'm solid on the basics of the set up for each model:
for teacher.rb:
has_many :subjects
for subject.rb:
has_many :teachers
and the teachers table should have a subject_id column and the subject table should have a teacher_id column.
What I'm not sure about is how to set up the views (and corresponding controller methods) to allow the addition of a Teacher to a Subject.
Any suggestions (or links to examples) are greatly appreciated. I haven't been able to find anything on this exact case.
current set up:
standard CRUD for a Student object
standard CRUD for a Project object
I'm likely missing something simple in how to tie these models together (other than the part of changing has_many to habtm) and getting records into the subjects_teachers table, and I still can't find a good example...
You need to build the relational table between them. It's impossible to have a many-many relationship without a rel table
First off though, it's a has_and_belongs_to_many :subjects and has_and_belongs_to_many :teachers (commonly referred to as habtm)
http://guides.rubyonrails.org/association_basics.html#the-has_and_belongs_to_many-association
run
rails g migration subjects_teachers
open up the migration:
create_table :subjects_teachers, :id => false do |t| # ID => FALSE = IMPORTANT
t.references :subject
t.references :teacher
# NO TIMESTAMPS
end
run
rake db:migrate and you should be set!
then
see these railscasts for setting up your controllers
http://railscasts.com/episodes/17-habtm-checkboxes
http://railscasts.com/episodes/47-two-many-to-many

Resources