ActiveRecord relations between models on different engines - ruby-on-rails

I'm building a Rails app that is probably going to have a large amount of models. For now let's say that i need to have Users, and each User will have a personal Blog
I've been developing the User-related part on a separate Engine (let's call it AuthEngine) that encapsulates the User model, authentication, etc. At the time this seemed like the best approach
In parallel, my colleague was developing another engine, to deal with the blog features (let's call it BlogEngine).
Now he needs to access user data which is stored in the other engine. He achieved this by defining:
BlogEngine.user_class = "AuthEngine::User"
This way, he can easily ask for user data, even though it isn't stored in the same engine. The problem comes when we want to define relashionships between models
He can easily say that each blog post belongs to a user
has_one :user, :class_name => BlogEngine.user_class
But as far as i know he can't specify that each User has multiple posts since the User model is within the other engine
The consequence of this is that he can't do things like #user.posts, but instead has to do Post.find_all_by_user(#user)
Is there a more elegant way to deal with this?
I also considered the possibility that each engine could simply generate the models inside the app, removing the encapsulation, but since the amount of models will grow quickly, i think this will make the app more of a mess, and not as much maintanable

I think you should reopen user class inside blog_engine in order to define has_many :posts relation and it should be appropriate way.

What about having a common models gem/engine with only the relationships as a dependency for your engines? This way you would have access to all relevant relationships in every engine.

Related

What do folks use app/services/ in rails applications

Every now and them I would come across this in the ruby on rails ecosystem:
class LocalizeUrlService
class Services::UpdateUserRegistrationForOrder
class ProductionOrderEmailService
UserCart::PromotionsService.new(
Shipping::BulkTrackingService.new(bulk_update, current_spree_user)
You can also see an example here
However, in the official examples of for example "Ruby On Rails Guides" I've never seen this. Which leads me to believe this is a concept coming from another language/paradigm different to Rails/OOP.
Where is this paradigm/trend coming from? Is there a tutorial/book
that these folks got influenced by? Are these folks holdouts from the SOA trend of a few years ago?
Is it a good idea to put code in app/service/blah_service.rb ?
If yes, what logic/code can be considered "Service" material.
Is there any kind code that would/wouldnt belong as a service?
What gem/plugin creates the app/services folder? The vanilla rails app doesn't ship with it at first.
sidetone: Personally I have issue with instantiating a service. I feel classes and instantiating is misused by a ametuer programmers. I feel a class and instantiating is "for a thing" and a service is something that "does"
so mixins/defs/include should be the way to go I feel.
Service objects are for things that don't fit well in the normal MVC paradigm. They're typically for business logic that would otherwise make your models or controllers too fat. Typically they have no state (that's held in a model) and do things like speak to APIs or other business logic. Service objects let you keep your models thin and focused, and each service object is also thin and focused on doing one thing.
Rails Service Objects: A Comprehensive Guide has examples of using service objects to manage talking to Twitter, or encapsulating complex database transactions which might cross multiple models.
Service Objects in Ruby on Rails…and you shows creating a service object to manage the new user registration process.
The EngineYard blog posted Using Services to Keep Your Rails Controllers Clean and DRY with an example of a service object which does credit card processing.
If you're looking for the origins, Service objects in Rails will help you design clean and maintainable code. Here's how. is from 2014 when they were coming on the scene.
Services has the benefit of concentrating the core logic of the application in a separate object, instead of scattering it around controllers and models.
The common characteristic among all services is their lifecycle:
accept input
perform work
return result
If this sounds an awful lot like what a function does, you're right! They even go so far as to recommend using call as the public method name on the service, just like a Proc. You can think of service objects as a way to name and organize what would otherwise be a big subroutine.
Anatomy of a Rails Service Object addresses the difference between a service object and a concern. It covers the advantages a service object has over modules. It goes into some detail about what makes a good service object including...
Do not store state
Use instance methods, not class methods
There should be very few public methods
Method parameters should be value objects, either to be operated on or needed as input
Methods should return rich result objects and not booleans
Dependent service objects should be accessible via private methods, and created either in the constructor or lazily
For example, if you have an application which subscribes users to lists that might be three models: User, List, Subscription.
class List
has_many :subscriptions
has_many :users, through: :subscriptions
end
class User
has_many :subscriptions
has_many :lists, through: :subscriptions
end
class Subscription
belongs_to :user
belongs_to :list
end
The process of adding and removing users to and from lists is easy enough with the basic create and destroy methods and associations and maybe a few callbacks.
Now your boss wants an elaborate subscription process that does extensive logging, tracks statistics, sends notifications to Slack and Twitter, sends emails, does extensive validations... now what was a simple create and destroy becomes a complex workflow contacting APIs and updating multiple models.
You could write those all as concerns or modules, include all that stuff into these three formerly simple models, and write big Subscription.register and Subscription.remove class methods. Now your subscriptions can Tweet and post to Slack and verify email addresses and perform background checks? Weird. Your models are now bloated with code unrelated to their core functionality.
Instead you can write SubscriptionRegistration and SubscriptionRemove service objects. These can include the ability to Tweet and store statistics and perform background checks and so on (or more likely put that into more service objects). They each have one public method: SubscriptionRegistration.perform(user, list) and SubscriptionRemove.perform(subscription). User, List, and Subscription don't need to know anything about it. Your models stay slim and do one thing. And each of your service objects do one thing.
As to your specific questions...
Where is this paradigm/trend coming from?
As near as I can tell, it's a consequence of the "fat model / skinny controller" trend; that's how I came to it. While that's a good idea, often your models get TOO fat. Even with modules and concerns, it gets to be too much to cram into a single class. That other business logic which would normally bloat a model or controller goes into service objects.
What gem/plugin creates the app/services folder?
You do. Everything in app/ is autoloaded in Rails 5.

RDBMS and Graph Database on the same Rails application

I'm developing a web app that has several "subapps" inside it. For some of them a RDBMS is clearly the weapon of choice. The issue is that lately I came with an idea for a nice little subapp whose logic and performance would benefit greatly from using a graph based database.
My problem is: This subapp is important and graph is the way to make it happen. On the other hand, the others are just fine on a RDBMS and in some cases migrating them to graph would add unnecessary complexity.
So, is it possible to have two heterogeneous database systems running on the same Rails app, perhaps using each controller to specify where to connect?
This is absolutely possible, but it's not something you'd handle at a controller level: it is the responsibility of each model class to define how its data is stored, for example by subclassing from ActiveRecord::Base or including Mongoid::Document or Neo4j::ActiveNode.
There's nothing particular you need to do. As long as the objects all conform to the active model interface (the above all do) then things like link_to 'Person', #person will still work.

Sharing AR models between Rails applications

I have a problem that I have been trying to solve for a while now. I have 4 different Rails applications using the same database, meaning they need to use the same models and have the same migrations. I initially solved the problem by creating a Rails engine packaged into a gem, which then carries all the models and migrations with it. Now I realize that there are pieces of functionality that only one application needs, but the others do not - like for example the admin application needs to have methods for providing sortable tables for all models - the other applications do not need this functionality at all.
So my idea was to find a way where I can provide the "base" models from the gem, while augmenting these base models in my specific applications to add additional functionality when needed. What I tried first was inheritance:
class User < Base::User
end
This does not work though, because now you have 2 User models in your load path (User and Base::User) and when querying associations, it always picks the "closest" class for the associated record class - meaning when you have an Model::Account which belongs_to :user, it will pick Model::User as the association class, not User. I tried reversing the AR type compute method but this only resulted in more problems.
I can technically provide all of my models from the base engine (gem), but the issue here is that how do i extend these models in my application? .class_eval feels really really dirty, inheritance does not work, providing base functionality as mixins means the "base" models do not feel and look like models at all. My goal would be to cause as little friction as possible for the other developers, I want them to be able to define their models in the gem like they do normally and then also have an easy way to extend that functionality in other applications.
Has anyone solved this problem before or have any suggestions? Or how do you guys solve this problem in your larger applications? Any help would be appreciated.
This is mentioned in the Rails guides. It describes class modification with the Decorator pattern.

Do I need to use multiple databases in Ruby on Rails application?

I am designing a CRM using Ruby on Rails. How do you think, do I need a separate database for every client company? Or should I use the same database for everyone?
If they are separate companies or competing companies (for say a white label CRM) you'll most definitely want to run separate instances because then you can credibly claim total sandboxing. Otherwise, if you ever inadvertently wrote code that somehow allowed the data from one to display for the other, the game is over. Your customer will run for the hills and tell everyone about their terrible experience with your product.
I would even suggest you run separate instances of your app for each customer. Heroku provides a super simple way to deploy RoR apps so spinning up a new one whenever you add a new customer is a reasonable approach. Of course, if you want a more turnkey solution that allows people to just sign up for an account, you will have to have a single instance that enforces customer data sandboxing in code. Obviously it can be done, but the separation isn't done at the infrastructure level which is ultimately the safest way.
Best regards.
I do it with a single database, like this:
class Company < ActiveRecord::Base
has_many :records
def recent_records
records.desc(:created_at)
end
end
class Record < ActiveRecord::Base
belongs_to :company
end
Then, in the controller, I can write:
#records = #company.recent_records
And pass that down to the views.
Hope this helps.

Ok use for Single Table Inheritence?

I have the need for a user and a contacts model, both have common attributes like first name, last name, sex etc. It thus seemed reasonable to create a model individual and have user and contacts inherit from that model.
class Individual < ActiveRecord::Base
end
class User < Individual
end
class Contact < Individual
end
My question is, what type of security problems would I open up by having the above setup? I'm using authlogic and all of its required fields are within the individuals table. The above method seems easy, and yet I'm worried I may open myself up to unforeseen security issues, any thoughts? Just to be clear a user can login and a user will have many contacts.
Using STI doesn't open you up to any new security issues as they are orthogonal..
However, STI may upset your DBAs as you will have null fields within that table. (attributes that are present in one sub classed model and not the other)
As from the design stand point, what are you achieving by having such a model relationship. If the only distinction between a User and a Contact is that a User can log in and can have numerous contacts then you can simply have an individual that can have many contacts (which map to Individuals) AND can possibly have login credentials
Remember that a contact may be shared by numerous individuals which stipulates either a has_and_belongs_to_many or has_many :through
hth
Conceptually I wouldn't mix them. The underlying intent/function of each model is substantially different, so the fact that they look similar in shape is a red-herring.
Potentially you'd run into a problem where someone could figure out a way to authenticate with a Contact, although it doesn't seem very likely, given its unlikely that you assign a username to a contact, or the secret hash stuff. But it would be possible if there is a bug in AuthLogic, or if someone tried hard enough to hack your setup and you weren't anal about protecting against injection attacks.
Also, there are performance reasons to not do this if your app grows a lot. Basically, I think its a bad design to have these two models rely on the same table for fairly superficial reasons.

Resources