How to share constans between Rails microservices? - ruby-on-rails

I have my main app and admin app built as a microservice, they are communicating via api. I want to share some constants between those two apps.
For example I have User model that can have role Owner or Regular. In admin app I can search Users and in this search I have dropdown with hardcoded user type (Owner, Regular). This is okay, but when I change naming (e.g. Regular -> Standard) I have to update my Admin app also.
To avoid changing admin app every time I change some core naming in my main app I want to somehow share those constants, so every change in main app will change Admin at the same time.
For now I found 2 solutions, both with pros and cons:
First is sending constants from main app to admin via json api. I have build a class that will fetch and store all constants in class variable, so it's available from every part of the the app. The good thing about this solution is performance (thanks to memoization it's only one api request) and it's easy to use later. The bad thing is I have no idea how to handle tests in this case. Of course I cannot let my tests make request to main app and stubbing this request makes the whole idea pointless, because after every change of constants in main app I will need to change tests in admin app.
Second approach I thought of is building a gem that will store all constants. It's very easy to implement, but this means I will need to make changes to this repo every time I want to change constants in main app. Also I work with big team and they won't be happy that they have to work on 2 repos at the same time.
What do you think about those solutions? First one seems to be perfect for me except tests, so maybe you have some ideas how to stub those constants without real values? I haven't tried gem solution yet so if you see some obstacles please let me know.
Maybe there is another better solution to this problem?

I'm not sure if there might be a better solution in terms of structure of the services themselves, for example one of the two services could have an API which returns the constants for the other to use.
But to answer the question you could use an engine, or more likely for simple sharing of constants, a gem. You can create a new gem with bundle gem <GEM NAME> and then add it to the Gemfile of both apps.
You will need to either have a gem server (e.g. geminabox) or just point directly to your code repo, e.g.
gem 'my-gem', git: 'git#my-server.com:git/my-gem`
Personally speaking I'd go with the API returning the constants because you might want to rewrite one service in another language in which case sharing a gem falls down.

write a gem and include it in the different services.
if they run on the same machines, another possibility is env vars.
Keep it in a DB/storage, i.e. memchache/redi. Load the relevant constants on each service init.

Related

Rails, handle two sites with different url and design but with the same db

I'm looking for the best way to solve a problem.
At this moment I have a site for a customer, example.domain.com
My customer ask to create another website with some changes in design, but the contents are the same of the first website. I don't want to duplicate the website, because every feature I add to the site A must be deployed also to site B, and I'm looking a smart way to handle the situation.
I need to keep two different domains and I need also custom mailers and other small tweaks in the controllers (and maybe in some models).
My idea is to put in application controller a before filter like this
before_action :detect_domain
private
def detect_domain
case request.env['HTTP_HOST']
when "example.domain.com"
request.variant = :host1
when "example1.domain.com"
request.variant = :host2
end
end
Then I use the variant with some conditional to choose the mailer, to customize the views and to apply some code changes.
Any other idea?
Using a before filter and a per-request variable like your proposal will work, with a couple caveats that I'll mention below. I'd recommend a tool like the request_store gem to actually store the per-request value of which "skin" is selected.
Now, for the caveats. First, the main problem with per-request variables is that your Rails app does not always exist in the context of a request. Background jobs and console sessions operate outside of the usual request/response flow of your app. You will need to think about what happens when your models or other non-controller/view code is executed when that variable isn't set. I would suggest simply not having your models depend on RequestStore at all -- have the controllers pass any request-specific information down into the models, if needed.
Secondly, it's not clear from your description if you want any data or logical separation between the two domains, or if you just want different look-and-feels. If the former, you might consider the apartment gem, which aims to make database multi-tenancy easier.
EDIT: I also want to mention that, as an alternative to the multi-tenant solution above, you also have the option of a multi-instance solution. Wherein, you use an environment variable to indicate which version of the site should be displayed, and spin up multiple instances of your app (either on the same server with a reverse proxy, or on separate servers with separate DNS entries or a reverse proxy). The downside is increased infrastructure costs, but the context problem I mentioned above no longer exists (everything always has access to environment variables).

Generate and send xml on model save

I need to generate xml from a model and send it to a web service on model save.
I'm sure this is a common case and should be straight forward. Just create a job on after_save callback that generates the xml and sends it to the endpoint.
Since I'm new to Ruby on Rails I'm not to sure how to handle this though. My questions are more about code organization. It's not unlikely that this api connection will be discontinued in the future so I need a clean modular way to get rid of it. Would it be best practice/convention to put this in a separate gem? Can gems actually add jobs to an existing rails queue? Can gems create migrations on install? I'll probably need to add a model to keep track of the api sync. How about dropping a table on gem uninstall? Or should I not use a gem for this at all?
I realize these are broad and basic Ruby on Rails questions but I'm kind of drowning in documentation. I'm just hoping for some examples and/or advice and maybe some pointers to relevant documentation. Thanks.
Gem installs/uninstalls are unrelated to apps, they live on different level and do not khow anything about your app code, db and so on unless they are loaded.
Gems for rails can provide rake tasks and/or generators, for example you can look into devise gem structure on how it does this.
But i'd advise against moving code to a gem before you know you have to, like for example when you need to reuse it in different project.
To reuse code inside single project - use mixins/concerns
In general:
don't make it a gem
it's an unnecessary world of pain, pretty much always,
never make anything a gem unless you intend to use it in the same way in 3+ applications
don't extract it into a concern either,
it doesn't seem very likely that you'll do the same operation on multiple models, code reuse seems to not be an issue here and you can actually reuse code more efficiently using service classes too
a lot of experienced Rails programmers regard this practice as concerning, forgive the pun. It seems this view is not shared by the Rails development team, but at least from my experience writing service classes seems like unnecessary complexity until your project grows enough and then you need to refactor a BUNCH of stuff and you realize you would have been better off ditching concerns from the start
use a service class instead and delegate the necessary methods to it from the model
this will leave you with a clean interface to extract later and will also allow you to use dependency injection if you need to mock your XML service for tests
don't tie API requests to model callbacks, there's usually just 2-3 places where you need to do something with the API and a bunch of other cases where that may not be the case, imagine:
tests,
or if you get a requirement to implement cache column,
or a "number of visits" column
or a gem like Paperclip that thought that it wanted to add something to the model but changed his mind and instead of that just touched updated_at
or any such trickery which will make you a grandiose API spammer and a sufferer of VERRRRY slow database updates
if you DO tie API requests to model callbacks,
then you better make sure that error handling is done properly and that timeouts etc don't rollback or delay your DB operation,
best way from my experience is to run these things through ActiveJob + one of the backends (though obviously not the :inline backend and ideally one of the backends which don't use your main database and allow asynchronous job submission - sidekiq comes to mind as a candidate)

How to run multiple "Applications" under one compoundJS instance?

I've been using nodeJS + expressJS for several years now developing a custom Application Platform for our organization. Our central framework provides a common set of services (authentication, language, administration, etc...) for any installed Modules/Applications under it.
I would like to switch our framework out with compoundJS. However I'm not familiar with the design constraints imposed by it (and Rails apps in general) and can't seem to figure out how to accomplish what I'm after.
I would like to only have a single server instance running: all
requests first process through our common authentication checking.
Then are passed on to an application's controllers.
I would also like to have each application separated out: preferably
under a separate site/applications/ directory. Each of these
applications could be designed using compoundJS normally. And I would like to install them like:
cd site/applications
npm install site-hr
npm install site-finance
npm install site-payroll
this would then have all the routes from /hr, /finance, /payroll operational.
How do I accomplish this?
Is there a way to get compoundJS to search the nonstandard /applications/* folders for models/controllers/views and load them while keeping the central /site configurations?
Or is there a better way?
Sorry for the late answer, but I needed something similar: I needed to put together tool applications in a portal.
I found a way to include child applications in a parent's Compound application as node modules. I wrote a guide on how to do it and sent a pull request to add it in the advanced folder of CompoundJS' guides. It is also available here. It requires a bit of work, but it works fine with 4 applications for us.
Hope it can help.
It's simple. Just use app.use in config/environment.js to map your sub-apps:
var mod = require('your-compound-module');
app.use('/subroot', mod());
When you visit /subroot/any-path, then it will be handled by /anypath route of your sub-app. Note, that you don't need any additional work on path helpers, as they will start with '/subroot' automatically (handled on compound side).
This is a good point, but we haven't seen any implementation yet. May be years later there would be some.
Using a proxy layer in front of the instances would be a general method, usually with Nginx, Vanish Cache etc. For the bleeding edge techs, I've heard Phusion Passenger has implemented Node.js support, but I haven't successfully tested yet. If you are familiar with Ruby, it would be a good try.
If you really want to construct a big project with many modules, you can try out some industrialized frameworks for instance Architect for Cloud9 IDE project.
For authentication, I think it's necessary to use independent methods in each application, but they can share with one user database.

How do you allow two Ruby apps to use the same model?

I'm working on a project that has two different Rails apps. One is the web app, and another is the API app. They both use the same database. How, or what, is the proper way for them to use the same models and schema? Currently, it appears as though the model files are copied from one app to the other, and they seem woefully out of synch when checking the differences.
A few options come to mind:
Git submodules. Store your models in a separate repo (or repos), and then include it as a Git submodule in each app. This allows you to reuse them without having redundancy, though you’ll likely have to ensure that you keep which revision each app is using in-sync.
Make it one app, with two distinct parts. Maybe your API could be a simple Sinatra (or other microframework) app, and reside within the Rails app, and then mounted to a path/subdomain. This may be the least amount of work and the simplest, but it’s also probably the most cluttered.
Use your own API. You already have an API, why not use it? Your front-end app can just talk to that and doesn’t have to have any the model logic inside it. This also has the nice side-effect of ensuring that your API works and is full-featured, as you’re now a consumer of it instead of just third-parties.
Which one you pick is up to you, though I happen to prefer the last. While it’s probably more work, you’ll likely end up with an overall better design by making your business logic external (and making MVC violations harder, if not impossible).

Multi domain rails app. How to intelligently use MVC?

Background:
We have app a, b, and plan to add more apps into this same application. The apps are similar enough they could share many views, assets, and actions. Currently a,b live in a single rails app(2.3.10). c will be similar enough that it could also be in this rails app.
The problem:
As we continue to add more apps to this one app, there's going to be too much case logic that the app will soon become a nightmare to maintain. There will also be potential namespace issues. However, the apps are very similar in function and layout, it also makes sense to keep them in one app so that it's one app to maintain(since roughly 50% of site look/functionality will be shared).
What we are trying to do is keep this as clean as possible so it's easy for multiple teams to work on and easy to maintain.
Some things we've thought about/are trying:
Engines. Make each app an engine. This would let us base routes on the domain. It also allows us to pull out controllers, models and views for the specific app. This solution does not seem ideal as we won't be reusing the apps any time soon. And explicitly stating the host in the routes doesn't seem right.
Skinning/themes. The auth logic would be different between the apps. Each user model would be different. So it's not just a skinning problem.
In app/view add folder sitea for sitea views, siteb for siteb views and so on. Do the same for controllers and models. This is still pretty messy and since it didn't follow naming conventions, it did not work with rails so nicely and made much of the code messier.
Making another rails app. We just didn't want to maintain the same controller or view in 2 apps if they are identical.
What we want to do is make the app intelligently use a controller based on the host. So there would be a sessions controller for each app, and perhaps some parent session controller for shared logic(not needed now). In each of these session controllers, it handles authentication for that specific app. So if the domain is a.mysite.com, it would use session controller for app a and know to use app a's views,models,controllers. And if the domain is b.mysite, it would use the session controller for b. And there would be a user model for a and user model for b, which also would be determined by the domain.
Does anyone have any suggestions or experience with this situation? And ideally using rails 2.3.x as updating to rails 3 isn't an option right now.
Devise does exactly this. You would do well to check out its architecture and apply that architecture to your own case.
You will have multiple separate Rails applications. The shared code will be a separate project, perhaps distributed as a gem or at least a separate Git repository. The shared code will include many controller actions and many view templates that are there to be sensible defaults, and which will be overridden in some apps but not in others.
All the custom code for application A will belong in a project solely devoted to containing the custom code for application A. It will be its own fully-functioning Rails application and will depend heavily on the majority of the sensible defaults provided by the shared code in the shared-code project.
I've used the theme support plugin before and dynamically set the theme based on the request uri:
http://mattmccray.com/svn/rails/plugins/theme_support
It will probably need some work to support Rails 2.3.
Update: Looks like there's a rewrite: https://github.com/dasil003/rails-multisite
Sounds like you want to make the 'base' app a plugin and use that in each of your site apps. You can use something like svn-extern so it's automatically updated whenever something changes.

Resources