In a Ruby on Rails application, where would the most logical place be to put a "file flag."
I am attempting to externalize configuration and allow the presence of a file to be the deciding factor on whether or not something shows on the webapp.
Right now, I have a file here:
lib/
deployment_enabled
Model deployment.rb
class Deployment...
...
def deployment_enabled?
Dir["#{Rails.root}/lib/deployment_enabled"].any?
end
end
Now this works of course, but i'm not sure this follows the MVC paradigms, since the lib directory should consist of scripts. I could put it in config, but again - not sure it belongs there as rails uses this for rails specific configuration, not necessarily the webapp.
I could of course put this in our database, but that require a new table to be created, and that seems unnecessary.
Where's the most logical place to put this flag file? Does Rails have a directory that's created during the generation to put these sort of files?
I suggest using the Rails tmp directory for this purpose. Then:
File.exist?("#{Rails.root}/tmp/deployment_enabled")
Phusion Passenger use this kind of mechanism too.
https://www.phusionpassenger.com/library/walkthroughs/basics/ruby/reloading_code.html#tmp-always_restart-txt
I recommend that you follow the Twelve-Factor App guidelines and keep your code separate from your configuration. In this case you are really talking about a simple boolean value, and the presence of the file is just the mechanism you use to define the value. This should be done instead through an environment variable.
Something like:
DEPLOYMENT_ENABLED=1 RAILS_ENV=production rails server
You would then use an initializer in Rails to detect the value:
# config/initializers/deployment.rb
foo if ENV['DEPLOYMENT_ENABLED']
The value can still be modified at runtime, e.g., ENV['DEPLOYMENT_ENABLED'] = 0.
Related
Rails 6 switched to Zeitwerk as the default autoloader. Zeitwerk will load all files in the /app folder, eliminating the need for namespacing. That means, a TestService service object in app/services/demo/test_service.rb can now be directly called e.g. TestService.new().call.
However, namespacing has been helpful to organize objects in more complex rails apps, e.g. API::UsersController, or for services we use Registration::CreateAccount, Registration::AddDemoData etc.
One solution suggested by the rails guide is to remove the path from the autoloader path in application.rb, e.g. config.autoload_paths -= Dir["#{config.root}/app/services/demo/"]. However, that feels like a monkey patch for shoehorning an old way or organizing objects into the new rails way.
What is the correct way of namespacing objects or a rails 6 way of organizing it without just forcing rails into the old way?
It is not true to say that Zeitwerk eliminates 'the need for namespacing'. Zeitwerk does indeed autoload all the subdirectories of app (except assets, javascripts, and views). Any directories under app are loaded into the 'root' namespace. But, Zeitwerk also 'autovivifies' modules for any directories under those roots. So:
/models/foo.rb => Foo
/services/bar.rb => Bar
/services/registration/add_demo_data.rb => Registration::AddDemoData
If you are already used to loading constants from 'non-standard' directories (by adding to config.autoload_paths), there's usually not much change. There are a couple of cases that do require a bit of tweaking, though. The first is where you are migrating a project that just adds app itself to the autoload path. In classic (pre-Rails 6), this allows you to use app/api/base.rb to contain API::Base, whereas in Zeitwerk it would expect it to contain only Base. That's the case you mention above where the recommendation is to exclude that directory from the autoload path. Another alternative would be to simply add a wrapper directory like app/api/api/base.rb.
The second issue to note is how Zeitwerk infers constants from file names. From the Rails migration guide:
classic mode infers file names from missing constant names
(underscore), whereas zeitwerk mode infers constant names from file
names (camelize). These helpers are not always inverse of each other,
in particular if acronyms are involved. For instance, "FOO".underscore
is "foo", but "foo".camelize is "Foo", not "FOO".
So, /api/api/base.rb actually equates to Api::Base in Zeitwerk, not API::Base.
Zeitwerk includes a rake task to verify autoloading in a project:
% bin/rails zeitwerk:check
Hold on, I am eager loading the application.
expected file app/api/base.rb to define constant Base
EDIT:
As clarified in comments, you actually don't need to add anything to autoload_paths. It's default behaviour for Zeitwerk in Rails when your place your code under some subdirectory in app.
Original answer:
I'm posting separate answer, but actually accepted answer has all the good information. Since my comment was bigger than allowed, I chose to add separate answer for those who are struggling with similar issue.
We have created "components" under app where we separate domain specific namespaces/packages. They co-exists with some "non-component" Rails parts, that are hard to move under components. With classic autoloader, we have added #{config.root}/app in our autoload_paths.
This setup fails for Zeitwerk and removing "#{config.root}/app" from autoload_paths didn't help. rmlockerd suggestion to move app/api/ under /app/api/api moved me thinking in creating separate 'app/components' and moving all components under this directory and add this path to autoload_paths. Zeitwerk likes this.
I have something defined as a app >model > something.rb
class Something < ActiveRecord::Base
end
Now I want to call this from somewhere else.
I read that w/o rails running, there is an ActiveRecord error(calling ActiveRecord as some unnamed variable);
How can I call , say
Something.create (x => y) from a helper file?
There is a nice blogpost which gives you the rundown on how to connect to ActiveRecord without rails.
In a nutshell, you need to require your AR files, and then call establish_connection with the correct configuration:
Active record works just fine and dandy without rails, but it needs
things to be just so. To start, get the gem and add it to your
gemfile. Next, make all of your classes inherit from AR like so:
Then you need to require it in the right places. Initially I was
requiring AR from the file containing each class, but this was messy
and confusing. So instead I moved to a solution that I’d seen Avi put
into the CLI playlister project, which helped clean things up: I made
a separate file for the entire environment called environment.rb, and
made it do all of the requiring for my min-app. The environment file
requires AR, points AR to the database file to use, and then requires
each of the models. Then the classes don’t have to require anything.
If i need to add (project specific) classes to my controler in rails, what is the correct way/place to put and "include" them/there .rb files? (quotes for: not the ruby keyword include)
I am new to rails, and did not find the correct way. LIB sounds like for more public libraries and - what I have learned - is not reloaded per default in dev mode.
sure, I could put all in controler.rb, but ...
the anser for me:
First: there are no rules, if you keep in mind (or learn like me) the rails rules:
NameOfCla -> name_of_cla(.rb) <-- not using class as word for clearence
name your class how you like:
class ExtendCon #<--- not using controller here for clearence
....
put it in a file extend_con.rb, wait for the path explaination, please. if you named your class 'MYGreatThing' it will be 'm_y_great_thing' (never testet that), so avoid chineese charachters
if your controller uses
#letssee=ExtendCon.new
rails learns that class and file (extend_con) on its own. i still did not figure out if a server restart is needed. (the first time)
choose the path to put the file: (I preferre Daves way) app/myexten or what you like, making it 'app' specific and still distquishes to standard rails 'things'
if you are not lasy like me (i put it in app/ontrollers)
put the path you have choosen into
config/application.rb like (comments are there to find it)
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/app/controllers)
config.autoload_paths += %W(#{config.root}/app/myexten)
this one workes for me in all modes including "developer" and i did not need to put "my own" things in app/lib
It depends.
I tend to put library code used explicitly (e.g., instantiated, injected, etc. into app-level artifacts) into app/xxx where xxx signifies the "type" of thing, like decorators, services, etc.
Magic stuff tends to end up in lib, like monkey patches, architectural-level artifacts, and so on.
Code anywhere can be added to the autoload paths, required automatically by an initializer, etc.
Rails 4 comes with an internal directory for controllers called concerns. You could try using that.
app/controlls/concerns
If you have concerns/foo_bar.rb, you include it as follows:
class FooController < ApplicationController
include FooBar
end
Models also have their own concerns directory. I find this approach useful, and it can be applied to Rails 3. You just have to add the directories to your load paths.
I'm learning rails and I've come across a little quirk that I can't seem to find the answer to anywhere:
Since I'm learning rails, I'll make a few tweaks to the code while the localhost is running (rails s) and then just refresh the browser to see if the change I wanted to make was accurate. This works for changes to the views, css, html, routing, etc.
But now I'm making changes to a controller file that is calling another ruby class that I wrote and when I make changes to the ruby class, they don't show up right away. The way I know this is that I use a variety of printf functions in the Ruby class to show the current state of things and if I add one and re-run, it won't show unless I shut the server down and restart it.
Any thoughts? Is this a known issue?
You must autoload the folder which contains your custom files:
# in config/application.rb:
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/extras)
There you write the folder which you want to be autoloaded.
WARNING: the naming is very important: files in there must be named as the class/modules they define (like models, controllers, etc):
foo.rb must define Foo costant
foo/bar.rb must define Foo:Bar costant
and you cannot autoload files which do not have this naming convention. The reason is linked to the autoload working: when in your code call f.e. the Foo constant, and the constant is missing, Rails tries to see if in its autoload paths there is a file that follows this naming convention, and if there is it loads it.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Ruby on Rails: Where to define global constants?
I am interested in doing this the "Rails Way" on a new application. I would also like to refer to constants in some sort of context to make the code more readable. I have an application where a user can request access to another users's data set. This AccessRequest can have one of the following statuses:
Review
Denied
Approved
I can see these values being used in reporting features in the future, so I want to make them constants in order to avoid any spelling or capitalization issues. I thought I would just put these in a constants.rb file in the config/initializers directory.
I would like to refer to these as AccessRequest::REVIEW. Since I already have a model called AccessRequest, does it make sense to put them there? Or wrap them in a class in a constants.rb file in the config/initializers directory? Which way is the Rails Way?
You don't need to use constants in Rails 3.It is better to use the Rails::Application singleton.
In your application.rb you can define your constante like:
module Yourapp
class Application < Rails::Application
config.access_request.review = 'xxx'
end
end
After in your code you can call
Yourapp::Application.config.access_request.review
After if you change value in each environment, You just define the config.xx in your config/environments/production.rb or other environment.
Belated reply, but posting as this answer still comes up in search results. Putting the constant in the model makes sense as the constant pertains directly to it. Using the Rails application config to store constants is incorrect.
As per the comment listed in application.rb:
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded
This is still valid as of Rails 3.