Getting from id column name to class name - ruby-on-rails-3

Given a column name in string format (e.g. "hamburger_id" or "foo_bar_id"), is there a standard way of getting to a class (or class name). In this case, Hamburger or FooBar?
The only way I could think of was this:
column_name[0, column_name.length - 3].camelize.constantize
I can assume (or stipulate) that the variable column_name always contains a string with "_id" at the end and that a corresponding class always exists.
If some one finds it relevant or is curious, I can elaborate on the "why", but I didn't want to clutter up the question with such details.

You could do the following (assuming the traditional Rails example of a Post model that has_many Comment models):
Model.reflect_on_all_associations.select{|a| a.foreign_key == 'column_name'}.first.class_name
This has the advantage that if your class names and foreign keys don't line up, it will still give the correct class name. For instance, if your foreign key is owner_id but the associated model is actually named User, this will return "User" instead of "Owner".
This still feels like a bit of a hack, but that's because it's not very Rails-like to get this information based on a column name - Normally you'd interact with the association directly (using reflect_on_association(:foos)).

Related

Rails; How to select for a single database value with a scope

I have a database called students with columns like so
In my rails model with a scope or function, I would like to select for the single age value based on the id. The id column is unique. So something like
scope :get_age, -> (id) { where(id: id).select(:age) }
However this does not seem to work. I would like for the returned value to just be the int 12. Using something like pluck ends up returning an array which I would like to avoid. How would I go about selecting for just the value of 12?
you know that for one id there is just one row (or no rows) so using where or pluck is not ideal, we want something it returns one row or nothing (find, find_by etc)
def self.get_age(id)
find_by(id: id)&.age
end
some_id = 10
User.get_age(some_id)
Scoping allows you to specify commonly-used queries which can be referenced as method calls on the association objects or models. With these scopes, you can use every method previously covered such as where, joins and includes. All scope bodies should return an ActiveRecord::Relation or nil to allow for further methods (such as other scopes) to be called on it.
Reference: https://guides.rubyonrails.org/active_record_querying.html#scopes
That's why is not working as you expected.
I think the best alternative would be to do as Ursus suggested. Create a method, a query object, etc.
You basically want to read an attribute(age) of your model object. For that, you don't need to write any specific method or scope.
Here is what you can do in the place where you have the id and you want to fetch the age for that record:
object = YourClass.find(id)
object.age

Yii CActiveRecord with Column Named "attributes"

I used the CRUD generator from a legacy database. When searching for a column value I get the following error:
htmlspecialchars() expects parameter 1 to be string, array given (/usr/local/share/yii/framework/web/helpers/CHtml.php:103)
The problem is that the model has an existing column named "attributes" which is creating a conflict. I removed the entry from the _search.php and commented out all instances in the model hoping to at least get it working but no luck. Any suggestions would be appreciated.
Thanks.
Every CActiveRecord instance (or CModel instance for that matter) has a getter/setter named attributes with which all the attributes can be set. This leads to a conflict because the generated crud code uses the attributes attribute expecting it works as described before.
The controller does something like:
$model->attributes=$_POST['ModelClassName'];
// or
$model->attributes=$_GET['ModelClassName'];
This is meant to set al the (safe) attributes of the model at once. Instead this overwrites the database attribute attributes of your legacy DB model.
This in turn leads to the error you describe, because $_GET['ModelClassName'] and $_POST['ModelClassName'] typically contain arrays of data.
I guess the easiest fix would be to directly call the setter function for the "normal" attributes behavior which would lead to replacing the lines mentioned above with something like the following:
// in the controller
$model->setAttributes($_POST['ModelClassName']);
// and
$model->setAttributes($_GET['ModelClassName']);
I think rest of the generated CRUD code (the views) could and should be left untouched to make it work.
If you want to know how and why this works, it's best to do some research into the __get and __set magic functions and how they're used in the yii framework.

OOP; Class containing data

This isn't really a serious OOP question, just want to get an opinion on the best way to take for my program.
Simple really: Let's say I have an employee class, and that class has the variables and methods that make up an employee.
My question is how would I store an 'employee data'(Data, not Object); It IS SIMPLE, I could just make a JOHN class that extends EMPLOYEE and add data there.
But I've read(actually not sure where) storing simple data in classes is not good?? Because if you extend a class, you should also add other functions in it.
Is there a disadvantage in the solution I proposed? Or is there a better way to 'store' data. NOTE: Assume I cannot open external files and I must add whatever Data I have in the code.
OK:
1) A "class" is a "template" for creating "objects".
A class doesn't contain any data - a class instance (a.k.a. an "object") contains data.
2) An instance of "Employee" might be an object with a member name (member data) of "John".
You wouldn't "extend" Employee to create "John"; you'd create an "object" with the name "John".
3) You might, however, extend employee for a "Manager" class.
4) To answer your original question, of course a "class" can be designed to hold "data". And every object of that class will contain that data.

serialized object not being converted

I have a Model called statistics which has a value field that contains Goals (a self defined class) data
class Statistic < ActiveRecord::Base
serialize :value
end
When I try to access the goals_against (an atr_reader of the Goals class) I get
undefined method `goals_against' for #<String:0x54f8400>
The value property contains following data:
--- !ruby/object:Goals \ngoals: {}\n\ngoals_against: 1\ngoals_for: 0\nversion: 1\n
In string format according to the debugger.
It seems that rails doesn't know this data is of type Goals.
Someone knows how to solve this?
Thanks
Three things:
First, where ever your Goal class is defined, make sure it is loaded. At some point Rails stopped auto-loading stuff in the lib folder. So where ever your extra classes are located, set them in config.autoload_paths (in config/application.rb).
Second, when you declare a column as serialized, you have the option of specifying the class. This is especially useful when you are working with a custom class and you want to make sure Rails does the conversion correctly.
serialize :value, Goal
Third, when you have a column that is serialized, make sure you have enough room for it. In other words, most of the time you're going to want that column to be "text" and not "string" in your schema (otherwise your sql engine will silently truncate anything too large to fit in a string column and you'll end up saving a broken object).

What is a "field"? "Field" vs "Field Value"

In a passport there is a field: First Name, and that field has a value John.
I assert that it is correct to describe the relationship as follows:
Field First Name:
Has a name (First Name).
Has a set of valid values (e.g. defined by regex [A-Za-z. ]{1,30}
Has a description (name that stands first in the person's full name)
And Passport is a set of pairs (field : field value), such that:
passport has a field "First Name"
passport has a value for field "First Name"
Point here is that it is incorrect to say:
"First Name value is John";
The correct way (conceptually/academically) is to say:
"passport has a value 'John' for field 'First Name'".
In practical terms it means (pseudo C#):
struct Passport {
Map<Field, object> fieldValues;
}
struct Field {
string Name;
string Description;
bool IsValidValue(object value);
}
Q: Does this make sense? Any thoughts?
This is pretty subjective and entirely context sensitive, and seems like a silly thing to nitpick over.
Correct or not, if I'm discussing "passport" with a co-worker, I'd throw something at them if they corrected me every time I said "firstName is 'john'", and told me to say it as "passport's firstname field is 'john'". You'd just come across as annoying.
Well..not really in c# see Scott Bellware's answer to my question about C# not being Object Oriented (kinda).
In C# passport is a class so it makes perfect sense to say
"The Passport has a field FirstName"
For a particular instance "FirstName value is John".
Here the first clause describes the class and the next one the object. In a more OO language like ruby I think saying "passport has a value 'John' for field 'First Name'" would be equivalent, you're just describing two objects - the Passport prototype, and the instance of it in the same sentence.
I'm getting pretty confused in it myself though. The question is oddly phrased since there would doubtless be much more to a passport than just its fields, for example a long-standing and persisted identity.
If you are going to model such thing, then you may take a look at reflection API of java or c#. It is pretty similar to what you described. Class has set of fields, field has name, type and other attributes, not value. Object is an instance of class and you can ask object for the value of specified field. Different objects of the same class have values for the same fields, so you may say they share fields. So if you are trying to model class-based OOP then you are probably right.
However this is not the only way to do OOP. There is prototype-based OOP which looks differently, as there are no classes, only objects, so objects contain field with values so there is not much difference if you say that object contain field and field has a value.
So the answer to "Does this make sense?" I think is "yes" because similar thing is in reflection and is used widely. If it is right or wrong - depends on your needs.
UPD: regarding "value = Passport[Field]" vs "value = Passport.Field.Value"
I'd introduce one more passport to make it clear
firstNameField = PassportClass.Fields["FirstName"]
myName = myPassport[firstNameField]
yourName = yourPassport[firstNameField]
assumes that both passport have same fields, that makes sense. Having different passports with different fields may have a sense, just a different one.
No. At least in OOP, it's the field's responsibility to retain the value. Although the object is responsible for ensuring that value is consistent with the other fields or the object's constraints, the actual "containing of the value is the field's job.
Using your example:
Field First Name:
Has a name (First Name).
Has a type (int, string, object)
Has a description (optional)
Has a value
And Passport is a set fields:
May define constraints on such a field as defined by the model, ensuring the value and the object's state as a whole is valid