Is there a way in Rails to manipulate database fields and corresponding accessor methods without the „nifty generators” ?
I want users, insofar they are privileged, to be able to manipulate the database structure, that is, at least, to add or delete columns. The privileged user should have the possibility to „Add new” some columns.
Say I have an Object/Table artist and it should “dynamically” receive columns as "date of birth", "has played with", "copies sold"
Not sure if it's a dup. It takes a preliminary decision whether Rails discourages from letting the user do this to begin with or or not. (if that's the case => certainly some noSQL solution)
In pure ruby at least it is easy to dynamically add an attribute to an existing Model/Class like this
Test.class_eval do; attr_accessor "new_attribute"; end
and
Test.new.new_attribute = 2
would return => 2 as expected
In order to create or manipulate a customized input mask / model: can I not manually go the same way the generators go and manually call ActiveRecord::Migration methods like add_column as well as create getter/setter-methods for ORM ?
If yes or no, in both cases, which are they to begin with?
Thanks!
I am not aware of any elegant way to allow an Object to dynamically create new columns. This would not be a good application design and would lead massive inefficiency in your database.
You can achieve a similar type of functionality you seek using ActiveRecord associations in Rails. Here's a simple example for your Artist model using a related Attributes table.
class Artist < ActiveRecord::Base
has_many :attributes
end
class Attribute < ActiveRecord::Base
belongs_to :artist
end
With this association, you can allow your Artist class to create/edit/destroy Attributes. ActiveRecord will use foreign keys in the database to keep track of the relationship between the two models.
If that doesn't work for you, your next best option is to look into NoSQL databases, such as MongoDB, which allow for much more flexibility in the schema. With NoSQL, you can facilitate the insertion of data without a predefined schema.
Related
I'm implementing a multi-tenant app with Rails. My approach is not to use the postgres inbuilt multi-tenant feature and add a column to record the subdomain. That is where the question is :)
Let's get this example
class Organisation < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :organisation
end
I'm thinking about two approaches here:
Approach 1
add a subdomain column only to organisations
pros - The way how relational databases should work \0/
cons - When I have more complex queries , that will make my code slow
Approach 2
add a subdomain column to both organisations and users
pros - This will make queries faster
cons - I'm going against the relational databases
So the question is, what sort of a method I should follow between above two, or are there a different approach that I didn't think about?
We run a multi-tenant Rails app with slightly fewer than 500 table-backed classes, and if I had to guess I'd say around 400 of them relate to client data.
Client-specific attributes are held in the Client model, but we add the client_id to every client table with a not null constraint on the database. Only a minority of them are indexed, though because they are generally only accessed through a parent record.
We do not have to worry about setting the client id because the model will generally have:
class Child
after_initialize do
self.client ||= parent.client
end
end
We add the client_id to many tables because we have a lot of code that does:
#books = current_user.client.books
... so in those cases we'll have an association directly from Client to the model, and client_id is indexed.
We add the client_id to all tables, though, because we very often, for operational or unusual reasons, want to be able to find all of the relevant records for a client ...
MarketingText.where(client: Client.snowbooks).group(:type).count
... and having to go through a parent record is just inconvenient.
Also, because we made the decision to do this on all client-specific tables, we do not have to make the decision on each one.
So to get to your question, what I would do is add the subdomain to the Organisation only. However, I would add the organisation_id column to every table holding Organisation-specific data.
If you have a lot of clients but you are going to be generally familiar with their subdomain, then I would write a meta-program method on the Organisation that lets you use:
Organisation.some_subdomain
... to get the required organisation, then find the child records (in any table) with an association directly from the Organisation model ...
Organisation.some_subdomain.users
Organisation.some_subdomain.prices
Organisation.some_subdomain.whatevers
my opinion will go Approach number one, couple reasons for this
using relational database provided with activerecord + scopes will make writing software easier, also if you have more objects under organization later for example transactions, items (beside users),
I have a project with multi tenant capabilities and below is sample of design in my project
class Company < ApplicationRecord
has_many :users
# transaction
has_many :transactions
has_many :journals , :through => :transactions
# item
has_many :items
# other has_many ...
end
and in controller you can use eager loading to minimize query (includes / joins)
#company.includes(:users).scope_filter_here.search(params[:q])
approach number 1 is more user friendly compared with approach number 2 as it's more simple to user writing your url address, the less url to type is better (personal opinion).
I'm having a trouble with opening AA edit-page for a model, which has a lot of associations.
What I had it's like 50 selects, opening at once. And this page turns to be deadly slow.
After reading this ActiveAdmin: How to handle large associations I considered to use select2 instead of usual select, but things get even worse.
That was because most of the time Rails spent in generating views, not in querying database. So with fancy select2 it reasonably spends even more time in views.
With that knowledge in mind, I decided to not have select inputs on that page at all. So I'll edit "main" object on that slow page, but connected with has_and_belongs_to_many objects should be edited separately.
But after that decision I've faced with a trouble: how should I edit tables with a complex primary key: not just id, but :person_id and :organization_id.
AA by default generates urls like that: /admin/person_organizations/:id/edit, but I need something like this: /admin/person_organizations/:person_id/:organization_id/edit
Any ideas?
ActiveAdmin should be able to handle custom primary keys by default. Just be sure that you add the definition to your model like this:
class Person < ActiveRecord::Base
self.primary_key = 'person_id'
end
After a while I've decided that I don't even need to have multiple keys here since Rails generates artificial id field for habtm tables. And as my goal was to edit this table, I've finished with standard ways of doing this.
I read many blogs, and one of the themes that comes across often is that concerns (at least the way Rails defines them) are damaging to software. On balance I agree - simply including behaviour into models is violating the single responsibility principle. You end up with a god-class that does too much.
But as with many of the opinions gleaned from blogs, an alternative architecture is rarely provided.
So let's take an example app, loosely based on one I have to maintain. It's inherently a CMS, as many Rails apps tend to be.
Currently each model has a large number of concerns. Let's use a few here:
class Article < ActiveRecord::Base
include Concerns::Commentable
include Concerns::Flaggable
include Concerns::Publishable
include Concerns::Sluggable
...
end
You can imagine that 'Commentable' would require only a small amount of code added to the Article. Enough to establish relationships with comment objects and provide some utility methods to access them.
Flaggable, allowing users to flag inappropriate content, ends up adding some fields to the model: flagged, flagged_by, flagged_at for example. And some code to add functionality.
Sluggable adds a slug field for referencing in URLs. And some more code.
Publishable adds publish date and status fields, with yet more code.
Now what happens if we add a new kind of content?
class Album < ActiveRecord::Base
include Concerns::Flaggable
include Concerns::Sluggable
include Concerns::Publishable
...
end
Albums are a bit different. You can't comment on them, but you can still publish them and flag them.
Then I add some more content types: Events and Profiles, let's say.
I see a few problems with this architecture as it stands:
We have multiple database tables with exactly the same fields
(flagged_by, published_on etc.)
We can't retrieve multiple content types at once with a single SQL query.
Each model supports the duplicated field names with the included concerns, giving each class multiple responsibilities.
So what's a better way?
I've seen decorators promoted as a way to add functionality at the point where it's needed. This could help solve the issue of included code, but the database structure isn't necessarily improved. It also looks needlessly fiddly and involves adding extra loops to the code to decorate arrays of models.
So far my thinking goes like this:
Create a common 'content' model (and table):
class Content < ActiveRecord::Base
end
The associated table is probably quite small. It should probably have some kind of 'type' field, and maybe some things common to absolutely all content - like a type slug for URLs perhaps.
Then rather than adding concerns we can create an associated model for each behaviour:
class Slug < ActiveRecord::Base
belongs_to :content
...
end
class Flag < ActiveRecord::Base
belongs_to :content
...
end
class Publishing < ActiveRecord::Base
belongs_to :content
...
end
class Album < ActiveRecord::Base
belongs_to :content
...
end
...
Each of these is associated with one piece of content, so the foreign key can exist on the feature's model. All the behaviour relating to the feature can also exist solely on the feature's model, making OO purists happier.
In order to achieve the kind of behaviour that usually requires model hooks (before_create for example) I can see an observer pattern being more useful. (A slug is created once a 'content_created' event is sent, etc.)
This looks like it would clean things up no end. I can now search all content with a single query, I don't have duplicated field names in the database and I don't need to include code into the content model.
Before I merrily unleash it on my next project, has anyone tried this approach? Would it work? Or would splitting things up this much end up creating a hell of SQL queries, joins and tangled code? Can you suggest a better alternative?
Concerns are basically just a thin wrapper around the mixin pattern. It is a very useful pattern for composing pieces of software around reusable traits.
Single Table Inheritance
The issue of having the same columns across several models is often solved with Single Table Inheritance. STI however is only really suited when the models are very similar.
So lets consider your CMS example. We have several different types of content:
Page, NewsArticle, BlogPost, Gallery
Which have pretty much identical database fields:
id
title
content
timestamps
published_at
published_by
# ...
So we decide to get rid of duplication and use a common table. It would be tempting to call it contents but that is extremely ambiguous - content of the content ...
So lets copy Drupal and call our common type Node.
class Node < ActiveRecord::Base
include Concerns::Publishable
end
But we want to have different logic for each type of content. So we make subclasses for each type:
class Node < ActiveRecord::Base
self.inheritance_column = :type
include Concerns::Publishable
end
class NewsArticle < Node
has_and_belongs_to_many :agencies
end
class Gallery < Node
has_and_belongs_to_many :photos
end
# ...
This works well until the STI models start to diverge for too much from each other. Then some duplication in the database schema can be a far smaller problem than the massive complications caused by trying to shoehorn everything into the same table. Most CMS systems built on relational databases struggle with this issue. One solution is to use a schemaless non-relational database.
Composing concerns
There is nothing in that says that concerns require you to store on the models table. Lets look at several of the concerns you have listed:
Flaggable
Sluggable
Commentable
Each of these would use a table flags, slugs, comments. The key is making the relation to object that they flag, slug or comment polymorphic.
comment:
commented_type: string
commented_id: int
slugs:
slugged_type: string
slugged_id: int
flags:
flagged_type: string
flagged_id: int
# ...
class Comment
belongs_to: :commented, polymorphic: true
end
module Concerns::Commentable
# ...
has_many: :comments
end
I would suggest that you look at some of the libraries that solve these kind of common tasks such as FriendlyId, ActsAsTaggedOn etc to see how they are structured.
Conclusion
There is nothing fundamentally wrong with the idea of parallel inheritance. And the idea that you should forgo it just to placate some kind extreme OO purity ideal is ridiculous.
Traits are a part of object orientation just any other composition technique. Concerns are however not the magic-fix all that many blog posts would have you believe.
Here is my situation. I have model called Account. An account can have one or more contracts. The problem is that i'm dealing with a legacy application and each account's contracts are stored in a different database.
Example:
Account 1's contract are in account1_db.contracts.
Account 2's contract are in account2_db.contracts.
The database name is a field stored in accounts table.
How can i make rails association work with this?
This is a legacy PHP application and i simply can't change it to store everything in one table. I need to make it work somehow.
I tried this, but it didn't worked:
has_many :contracts, :conditions => [lambda{ Contract.set_table_name(self.database + '.contracts'); return '1' }]
Any ideas?
Why isn't database migration an option?
You're approaching this the wrong way. You want the two systems in your integration to be loosely coupled. By trying to get the two associated, you're creating an array of interdependencies that will later come around to backstab you. The approach you are trying creates tight coupling and reduces cohesion.
But, to directly answer your question, see below. Once again, I don't recommend implementing what I say below, but it would technically be a solution.
The first thing is that rails associations work only with foreign key. In fact, all database associations work this way. There isn't a ActiveRecord method of association without foreign keys as it defies what it means to associate two objects.
So you're not going to get it done with a has_many association. Instead, I would just manually create a function on your Contract model that simulates a has_many association.
class Account
memoize :contracts
def contracts
# Load from other database in here
end
def contracts=
# Push to other database in here
end
end
I'm writing a quick app for a user to track their daily bills (for money tracking purposes). I want the user to be able to define their own categories that a bill can be applicable for. I'm trying however to decide the best way to model this and also validate categories as unique.
My initial thought was this:
class User
include Mongoid::Document
embeds_many :bills
field :categories, :type => Array
end
class Bill
include Mongoid::Document
embeded_in :user, :inverse_of => :bills
field :category
index :category
end
So a user can add categories, just as strings, and when they add a bill, they'll choose from their available categories for the bill.
So, a couple questions:
Does this seem like the proper design? I Don't think it's necessary to define an actual category model as it's literally just a string used to index bills on, but I'm not sure if there are other benefits to a separate model
How do I validate_uniqueness_of :categories in my user model. I don't think it works on array items like this, but I could be wrong. I don't want a user to create categories with the same name. I suppose this might be the advantage of a separate model, embedded in the User, but again it seems like more work than necessary.
Can someone tell me my best options here to validate that a user has unique categories (but users can have the same categories, i obviously don't care about that, just unique in the scope of a single user)
[Update]
The design seems proper. In a Rails specific way how would you validate the uniqueness? When adding a category pull the list and do an indexOf check to ensure it doesn't exist. If it does just bounce back an error.
I'm not a Rails guy, let me know if I'm off track or something.
I'm not sure MongoDB would be the best choice of storage engines for that. You would be better off using MySQL with a categories table.
Knocks against MongoDB:
Not ACID transactions
No single server durability
Not relational (you want relational for a bill tracking application)