I'm just getting rolling with RoR so I'm sure this is pretty basic. Let's say I have two models: Account and Transaction
class Account < ActiveRecord::Base
has_many :transactions
end
class Transaction < ActiveRecord::Base
belongs_to :account
end
What methods (for each model) become available/are auto generated after I make this association?
Thanks
It depends. Some methods (e.g. Account#transactions, Transaction#account) will be there from the get-go. Others will be created as needed (via a method_missing hook) such as dynamic finders. The exact list can depend on other factors, including things like acts_as, etc. used elsewhere.
Are you concerned about which ones are created or about what the full possibilities are?
-- MarkusQ
P.S. See here for more:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
run script/console from the root of your rails app and then you can explore all of the magic model methods like this:
> account = Account.new
> account.methods
> ...[long list of methods]...
> transaction = Transaction.new
> transaction.methods
> ...[long list of methods]...
You'll get a long list of all of the methods for the object including the generated ones.
All of the methods will be listed without line breaks - and with 100+ methods it can be hard to read.
You can use an .irbrc file in your home directory with some custom methods to format output in irb so it is easier to read.
going on inkdeep's answer : you can output something like this in your view so it's formatted:
#transaction.methods.each do |method|
method + ""
end
I'm using HAML so syntax is a bit different, but just look up "do"
Related
I'm trying to delete all the organizations that no longer have any users.
Using the below code, I can find all the records I wish to delete:
Organization.includes(:users)
.where(users: { id: nil })
.references(:users)
When I add delete_all, I get the same error I would get if I didn't include references:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "users"
I could probably write the solution in pure SQL, but I don't understand why Rails isn't keeping the reference to users when I add the delete_all statement.
Here are some more details:
Organization:
has_many :users
User:
belongs_to :organization
I've found the includes useful only for eager loading (and it can rarely handle my cases), and when coupled with references it generates something completely insane (aliasing every single field with something like tN_rM) even though it actually does a LEFT OUTER JOIN... Which could help if it didn't vanish once delete_all appears!
I've found that it's much clearer and simpler just to use exists. It's Arel (and there's no point in avoiding it, its under the hood of ActiveRecord anyway), but it's such a tiny portion that it's barely noticeable:
Organization.where(
User.where('users.organization_id = organizations.id').exists.not
)
Or, if this string of SQL doesn't look nice to you, use a bit more Arel, so it gets noticeable:
Organization.where(
User.where(organization_id: Organization.arel_table[:id]).exists.not
) # I tend to extract these ^^^^^^^^^^^^^^^^^^^^^^^ into local variables
That handles chaining .delete_all on top just fine, since it's not (syntactically) a join, even though it's effectively equivalent to one.
The magic behind this
SQL has an EXISTS operator that is similar in functionality to a join, except for inability of selecting fields from a joined table. It forms a valid boolean expression which can be negated and thrown into WHERE-conditions.
In the "SQL-free" form I'm using an expression "column of a table", which turns out to be usable in Rails' hash-conditions. It's an accidental discovery, one of the few uses of Arel that does not make code too bulky.
I'm not sure how you plan to implement this in the MVC framework, but it seems clean to do the organization purge via model action. Whenever a user is deleted, check to see of the organization has any remaining members.
in the User.rb
class User < ActiveRecord::Base
before_destroy :close_user
...
def user_organization
Organization.where(user_id: id)
end
private
def close_user
unless user_organization.users.any?
user_organization.destroy
end
end
end
Added To apply callback delete solution to users being member of many organizations
If the user has multiple organizations
class User < ActiveRecord::Base
before_destroy :close_user
...
def user_organizations
Organization.where(user_id: id)
end
private
def close_user
user_organization.find_each do |organization|
unless organization.users.any?
organization.destroy
end
end
end
Caveat: this is not tested, didn't fail syntax. I don't have the data to test it fully but I think it will work. But it means running this action after every user delete, which is a system architecture decision. If it's an option, it might be worth a try.
There are three questions here, all of them set in bold.
I have a personnel database where historical data is also stored.
Each table has two columns start and stop which indicate the period during which this fact is valid.
When any field of a record changes, a new record is created with start=today, and stop=nil,
and the old record is given a stop=today-1 (or whatever the effective date of the change is instead of today).
When a record is to be deleted, instead of deleting, we simply set stop=today.
This way, it is easy to
give a history of any employee
give a snapshot of what things looked like on a particular date
Firstly this pattern seems common enough that probably I should not need to re-invent the wheel.
Yet in my search all I found were some gems called auditable or papertrail.
From a first glance, they did not seem to do what I want, but perhaps I am mistaken.
Do you know of a gem that does something similar?
If I don't find a solution, what is enough for my purposes is to have a few methods which works for all my models, like
def when(date)
self.where("start <= ? and ? <= stop", ondate, ondate)
end
I thought about inheriting
class MyRecord < ActiveRecord::Base
def when ...
end
end
and having all my models inherit from MyRecord, but this broke everything. Why?
I could just add my methods to ActiveRecord::Base, but that seems wrong.
What is the proper way to add a method to all models?
Answer
Parts 2 and 3 of this question have been answered before here. Since that contains Josh's answer (which I +1ed), as well as why inheriting from MyRecord doesn't work, I did not accept Josh's answer.
I still have not located a gem which maintains a good history, but two out of three ain't bad...
This sounds to me like an excellent opportunity to use an ActiveSupport::Concern. You could write something like:
History.rb
require 'active_support/concern'
module History
extend ActiveSupport::Concern
included do
def self.when(date)
self.where("start <= ? and ? <= stop", ondate, ondate)
end
end
end
Your model
class SomeClass < ActiveRecord::Base
include History
end
You could then call SomeClass.when(some_date_string) on any models that you need to include history on.
This question relates to using Ruby on Rails 3 with MongoMapper and EmbeddedDocument. Also, the ManyAssociation.
http://mongomapper.com/documentation/embedded-document.html
The MongoMapper examples (in the link above) show two separate classes:
class Order
include MongoMapper::Document
many :line_items
timestamps!
end
class LineItem
include MongoMapper::EmbeddedDocument
key :name, String
key :quantity, Integer
end
But this pollutes the global namespace with LineItem. Out of context, LineItem for what? And what if I want another model, say WishList, to also have a LineItem set?
So it is possible to embed the LineItem class inside Order, like this:
class Order
include MongoMapper::Document
many :line_items, :class_name => "Order::LineItem"
timestamps!
class LineItem
include MongoMapper::EmbeddedDocument
key :name, String
key :quantity, Integer
end
end
While this may be technically fine (yes?), will I run into design issues later on? Does it just make the code too ugly? Too complex?
Presumably, the existence of this in the Ruby language means someone thinks it's an ok idea?
One thing I always liked about Django is how it uses "apps" to group related model classes (and separate the namespaces). So my code above is also achieving that in Rails.
I don't see any technical problems to either approach. The problem I've run into is that when a class is embedded in it's parent and is also in the same file, I'll forget it's there. So if your namespaces it as Order::LineItem you can make an "order" folder in your "models" folder and put "line_item.rb" in there.
The other trouble is if you want to have a controller for Order::LineItem, you also have to namespace it and put it in a folder, and in the router it will look like:
resource :orders do
resources :line_items, :controller => "order/line_items"
end
Unless you know your app is going to have multiple types of line_items, I'd recommend not namespacing it—you could be over-coding if you did. For example, if you were later to need two types of line_items you might even find that some of your code could be re-used between models—and if you namespaced your first line_item you may find yourself de-namespacing it.
In Python, namespaces are considered a great thing that there should be more of. But when you think about the global namespace of your Rails app, it's not that cluttered. Gem authors are pretty good about keeping all of their classes namespaced in their one gem module, so in your Rails app you're going to have one namespace for each gem you're using (Rails itself is 5 gems, not sure how many global constants though), plus and a bunch of files included by Rails (e.g. SecureRandom). It turns out to be really nice to have those files "just there" and I've found in practice that namespace collisions are rare and you can easily work around them. I've only run into a namespace issue maybe once, but several times I've accidentally defined a "send" method on a model—a much more common problem with similar repercussions.
I'm working on modifying part of an existing Rails app to use the Class-Table-Inheritance gem (https://github.com/brunofrank/class-table-inheritance). All's well, except that I have defined some instance methods in my superclass -- Person, which all subclasses need to be able to access. For instance, full_name, which returns the concatenated first and last names, or cite_name, which returns the first initial and last name. Since the CTI gem doesn't actually use Ruby inheritance (all subclasses still inherit from ActiveRecord::Base, and the gem does some funky voodoo to link the ActiveRecord fields together), I can't access these methods in the subclasses I've created. Any thoughts on working around this? I'm not interested in STI, but I'm willing to either fork and hack on this particular CTI gem, or look at other solutions.
try http://peterhamilton.github.com/citier, it's based on CITIEsForRAILS, I forked it & put it through rapid development and various other changes. It fixes most, if not all the bugs it had and works so simply.
Just off the top of my head, did you see that in the migration tables of your subclasses you need to have the line:
create_table :videos, :inherits => :product do |t|
and also in your Model.rb files
class Product < ActiveRecord::Base
acts_as_superclass # I'm guessing you might be missing this line??
end
class Book < ActiveRecord::Base
inherits_from :product
end
I think that the brand new CITIEsForRAILS gem (Class Inheritance & Table Inheritance EmbeddingS For RAILS, see https://github.com/altrabio/CITIEsForRAILS ) does exactly what you desire. This gem extends CTI, STI, & Multi Table Inheritance while preserving Ruby class Inheritance.
Say I have a system table 'categories' with 2 fixed records. The user will not be allowed to delete these 2 but may wish to add their own to extend their list. I need to be able to pull out the 'garden' category at certain times, e.g when creating a garden project.
A class attribute reader that returns the garden instance would do the job, but I would like to know how this can be improved with caching?
I believe memoization would only work per process which is almost pointless here. I would like it to be set once (perhaps the first time it's accessed or on app start-up) and just remain in cache for future use.
Example setup:
class Project < ActiveRecord::Base
belongs_to :category
end
class Category < SystemTable
cattr_reader :garden
def self.garden
##garden ||= self.find_by_name('garden')
end
end
How about the approach shown below, which will retrieve the garden instance once when the Category class is loaded:
class Category < SystemTable
GARDEN = self.find_by_name('garden')
end
Now whenever you need the garden category you can use Category::GARDEN.
Interlock, a plugin which works with memcached, will automatically cache any instances that are made by a straight 'find' using id. Similarly, you can bypass interlock and just cache the object in memcached manually. Class-level variables is a dirty and potentially bug-causing solution (and i'm not even sure if it works). There's plenty of info on installing and using memcached/memcache-client on the web.