Is there a safe spot to store dynamically created Rails models? - ruby-on-rails

I'm using the following Rails 3 model to dynamically create models:
# encoding: UTF-8
require_dependency 'read_only'
require_dependency 'readings_methods'
class Reading < ActiveRecord::Base
self.abstract_class = true
include ReadOnly
include ReadingsMethods
##mutex = Mutex.new
##children = {}
def self.with_table(t)
##mutex.synchronize do
child = ##children[t]
if child.nil?
child = Class.new(self) do
self.table_name = t
end
##children[t] = child
end
child
end
end
end
Debugging shows, however, that ##children is reset on every page load, thus making every page load very, very slowly. How do I prevent this? And what is causing it? Unless I memoize children, Rails goes into infinite loop, which leads me to believe that it reloads at least some classes whenever I create a new model, or something like that. How do I prevent this behavior?

Rails reloads code for you in development mode so that you don't have to restart the rails server every time you make a change (in rails 3.2 it tries to only reload the code that has changed).
When a class is reloaded, rails unsets the constant and loads a fresh copy, so in particular the new one will have a fresh set of class variables.
ActiveSupport::Dependencies.autoload_once_paths and ActiveSupport::Dependencies.autoload_paths control what is reloaded in this manner. You should be able to add the (full) path to your file to autoload_once_paths to prevent that one file from being reloaded (but you will then have to restart rails so see any changes made to it )

Related

Rails 6: How to load global variables only once?

My app uses global variables like logos, app name, etc retrieved from the database and shown on different controllers and views. I put it in ApplicationController to be available to all, but I find that the individual controllers repeat the same query sometimes.
class ApplicationController < ActionController::Base
$image = Setting.find_by_name('image').value
$city = Setting.find_by_name('city').value
$currency = Setting.find_by_name('currency').value
end
Is there a way to make the same variables available to all controllers (and users) with just a one-time query with the variables saved on memory, such as when the app starts up?
You could attempt to use initializers.
Rail will load all files in config/initializers/ folder when the server starts up. This can work as a place to initialize application-wide variables. We could create a file inventory.rb file in the initializers directory:
at config/initializers/inventory.rb
module Inventory
class Count
Orders = Order.all
end
end
Inventory::Count::Orders
# => "[your orders will show here]"
These will only be loaded once when the server is started or restarted. As such this works well if the values you need won't change. If they will change I don't think there is a good way to avoid running multiple queries.
Whats about caching? Rails is using SQL Caching and you can use Low Level Caching. See the guides (1.7 and 1.8): https://guides.rubyonrails.org/caching_with_rails.html#low-level-caching

Rails STI and multi-level inheritance queries

In my database I have a table people, and I'm using single table inheritance, with these classes:
class Person < ActiveRecord::Base
end
class Member < Person
end
class Business < Member
end
The queries it generates confuse me. What I want is for Member.all to return all Businesses as well as any other subtypes of Member. Which it does, but only if I've accessed the Business class recently. I assume it's because my classes aren't being cached in development mode (for obvious reasons), but it still seems like strange/buggy behaviour.
Is this a bug in rails? Or is it working as intended? In either case, can anyone think of a good fix for development purposes?
This is intentional behaviour—the official Rails guide on Autoloading and Reloading Constants explains it pretty well in the section on Autoloading and STI:
…
A way to ensure this works correctly regardless of the order of
execution is to load the leaves of the tree by hand at the bottom of
the file that defines the root class:
# app/models/polygon.rb
class Polygon < ApplicationRecord
end
require_dependency 'square'
Only the leaves that are at least grandchildren need to be loaded this
way. Direct subclasses do not need to be preloaded. If the hierarchy
is deeper, intermediate classes will be autoloaded recursively from
the bottom because their constant will appear in the class definitions
as superclass.
So in your case, this would mean putting an require_dependency "business" at the end of your Person class.
However, beware of circular dependencies which can possibly be avoided by using require instead of require_dependency (even though it may prohibit Rails from tracking and reloading your files when changes are made—after all, require_dependency is a Rails-internal method).
By default, Rails is not eager loading your classes in development. Try changing the following line in your config/environments/development.rb:
# Do not eager load code on boot.
config.eager_load = false
to:
# Do eager load code on boot!
config.eager_load = true

new to rails. going crazy when just trying to add a class and use it

hi i know i am new to rails.
i came from ASP.Net mvc
but although most of the stuff in rails are very easy to do sometimes the small things which are easy in .net makes you crazy in rails.
i have a rails app and im just trying to add a class. and use it in my controller. this class is just for holding data. not from the db. just a simple class for me to use
so i added the class file first in the "/libs/assests" folder. then i read i needed to add a line to the application.rb file that says to load the files from there
so i did..
config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]
this still didn't work..
so ive put my class file in the regular Models folder. but it seem its still isn't working
this is my class code:
class Person
attr_accessor :name, :role
def initialize(name, role)
#name = name
#role = role
end
end
and in one of my controller is try to do this:
Person.new("name", "worker");
but i get this error:
uninitialized constant MainController::Person
what is the big deal?.. why is this so complicated to add a public class to a project?
thanks
You have to require the .rb file where the class is specified, you can do that with "require" or "require_relative":
http://rubylearning.com/satishtalim/including_other_files_in_ruby.html
In your Rails.root start up the console:
rails c
Just reference the class name:
Person
What do you see?
Without know much more, it looks like your load path might not be right. See what's in there:
puts $:.join("\n")
Lastly, brute forcing it might give you more info about the problem:
require Rails.root.join("app","models", "person")
This loads the file manually and skips the rails auto loading magic.

Modifying a ruby class doesn't work as expected when running Spork

I have a plain Ruby class in my Rails app that I'm reopening in a test environment. It basically looks like
class A
def get_dependency
B
end
... some other methods ...
end
And in my test environment in cucumber (in a file loaded from features/env.rb) (and a similar place for rspec) I do
class A
def get_dependency
MockedB
end
end
This works fine in normal runs, but when I have Spork running, it fails strangely. Class A's get_dependency method is overwritten properly, but all its other public methods are now missing. Any ideas?
I'm assuming this is related to load order somehow, but I didn't get any changes when I moved the require for my file out of the preload section of Spork.
This isn't a great answer, but it's a workaround. Instead of reopening the class I just modified a singleton instance. The code is basically the same, except I added an instance method on A:
class A
def instance
##instance ||= A.new
end
end
Then in my test code I modified the instance
instance = A.instance
def instance.get_dependency
MockedB
end
And I just had to ensure that my actual code was always calling A.instance instead of A.new.
One possible scenario is that A is set to get autoloaded, but when you define the override for it in your cucumber environment, you do so before it has been autoloaded; since A now exists, it will never get autoloaded.
A possible solution, which invokes the autoloader before overriding the method is this:
A.class_exec do
def get_dependency
MockedB
end
end
It will raise a ConstMissing if A cannot be autoloaded at that point (perhaps the autoloaders have not yet been set up).

Differences in subclasses function between Rails 2 and 3

Let's say I have something like this
class Major < ActiveRecord::Base
def self.my_kids
self.subclasses.collect {|type| type.name}.sort
end
end
class MinorOne < Major
end
class MinorTwo < Major
end
In Rails 2.3 I could call Major.my_kids and get back an array of the subclass names, but in Rails 3.0.3 I get back an empty array, unless I load the subclasses first. This seems wrong to me, am I missing something or is this new to Rails 3?
There's no difference that I know of between Rails 2 and 3 concerning the use of the subclasses method. You might have believed it was working previously because the subclasses were already loaded. As Rails load most files dynamically a parent class couldn't know about any class derived from it unless it is defined in the same file. The easiest way to ensure all models are loaded, you can simply call require on all files in the app/models directory:
Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file }
One other thing to note is that the subclasses method doesn't work after issuing the reload! command in the Rails console.
The reason why you're getting an empty array in Rails 3 is likely because Rails 3 uses autoloading.
If you open the Rails console and you reference the name of the subclasses, then run your 'subclasses' method on the parent class, you'll see it works. This is because Rails 3 only loads the classes into memory that you've referenced, when you reference them.
The way I ended up forcing my classes to load from a library I created under /lib was with the following code I added into the method that depends on those classes:
# load feature subclasses
my_classes_path = File.expand_path(File.dirname(__FILE__)) + "/my_classes"
if File.directory?(my_classes_path)
Dir.glob(my_classes_path + "/*.rb").each do |f|
load f
end
end

Resources