in a project we have several customers/clients for a Rails 3 application, where some of them need specific code although we try to solve as many requirements as possible through configuration of the application.
We want to have all customer code in a single branch but in separate directories. Therefore I am now experimenting with autoload_paths:
config.autoload_paths += Dir[Rails.root.join('customer', 'abc', 'app', 'models')]
This works for new files/classes/models, but it is not possible to modify existing models/classes with this approach. For example I want to add a single method to app/models/test_model.rb from customer/abc/app/models/test_model.rb:
class TestModel
def self.test_me; 123; end
end
Unfortunately this overrides the whole class/model and not only this single method. I know that I can do this in Ruby even for core classes like String:
class String
def to_bla; "bla"; end
end
Is it possible to have this behavior also for rails models or is there any better way to separate customer specific code from the rest of the project? I would prefer a solution where I don't have to insert requires or includes in the files I want to customize. It would also be great when rails automatically reloads files for changes in customer specific files in development mode.
Related
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.
I'm trying to add a method to the DateTime class like so:
class DateTime
def ymd(sep = "/")
strftime("%Y#{sep}%m#{sep}%d")
end
end
I put this in #{config.root}/lib/datetime.rb and updated the autoload_path to include #{config.root}/lib (since that seems to go in and out of the conventional autoload path). That didn't work, so I also tried putting it in a random directory (#{config.root}/blah and added that path to the autoload_paths line in the config).
In all of the above cases, I'm only able to use the new method in the rails console if I require 'datetime' first, and I'm not able to use it in controllers or view templates no matter what I do.
So,
Should the file be called datetime.rb or date_time.rb? (I've tried both so far and neither are currently working)
Where should I be putting this file so I can use the new method in models, controllers and views?
Any idea why I can require it in the console, but it doesn't autoload there?
The app is currently running rails 3.2.21, but I'll switch to rails 4 at some point so answers for either version are appreciated.
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.
Where should I put my simple classes that are not models (they don't represent any data) and provides only some functionality?
Like for example:
class FileUploader
def save_uploaded_file(filename, tempfile)
path = Rails.root.join('public', 'uploads', filename)
File.open(path, 'wb+') do |file|
file.write(tempfile.read)
end
end
end
What this does is just simply copies file to predefined place.
Do I must to place this under models? Or do Rails have some other location more suitable for those kind of classes? I tried to put in lib before, but it was just too much of the pain as Rails cached those files inside lib directory (edited config to not cache files, but it still cached inside lib).
So, I placed those files in models but testing becomes very painful (I'm just starting Rails, maybe I'm doing something wrong with the tests) as rake spits out errors about not having set up a test database (I want to test classes that are not related to database in any form or shape - why do I need to set up a database?).
I've taken to adding app/lib to some of my projects to accommodate this, but there's also the new Rails 4 concerns locations like app/models/concerns or app/controllers/concerns depending on where this sort of thing is used.
You're right that it's best to avoid putting non-model classes in app/models.
For service-type objects I put them in app/services.
For model-type objects I put them in app/models. I do not think it is necessary to inherit from ActiveRecord to be considered a model.
I think your object classifies as a 'service object' since it is designed to wrap the service of uploading a file, it isn't really a domain object like a model would be.
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.