rails 3 - form_for first parameter string with brackets - ruby-on-rails-3

ActionView::Base instance method form_for can accept a string as its first argument. Sometimes you want to update multiple objects at once within an edit form. So you can pass a string as the first argument to form_for with a set of square brackets concatenated with the object name (e.g. person[]). Then you can iterate through the array of people objects and invoke the FormBuilder instance methods on it, such as text_field, passing the attribute associated with the person object. That's fine and understandable.
However, I was looking through the source and I come across this:
if #object_name.to_s.match(/\[\]$/)
if object ||= #template.instance_variable_get("##{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
#auto_index = object.to_param
else
raise ArgumentError, "object[] naming but object param and #object var don't exist or don't respond to to_param: #{object.inspect}"
end
end
#object_name is an argument passed into the constructor of FormBuilder, and it refers to that first parameter passed to form_for, such as user[]. Here we check if the string contains square brackets and if it does, we check the template ActionView::Base to see if it contains a #user instance variable (Regexp.last_match.pre_match returns user and then we just interpolate the # to build an instance variable). Remember that instance variables are copied from the controllers to view layer, so if we have it defined in the controller, it will be available in ActionView. So let's say that because we are in our edit form, we do have a #user object that exists. Here, we invoke the to_param method on it, which will return the id of the record as a string (assuming to_param has not been overwritten) and assign it to the #auto_index instance variable.
Here's my question. let's say that #user does exist and has an id of 1, which is assigned to #auto_index. But if we are checking for a string with square brackets user[], obviously we are expecting an array of ids, so why does it only check for one object and assign it to auto index, rather than grabbing the collection of user objects and assigning all their ids to auto index?
Please refer to line 1197:
https://github.com/rails/rails/blob/master/actionpack/lib/action_view/helpers/form_helper.rb

Related

Smalltallk - How can I get an Array (or Collection) of the all the Instance variables in an Object (the current Instance) of a Class?

Let's say we have a Class and we instantiate it, creating an Instance of that Class. This Instance has a number of (instance)variables, defined by the class, that I need to use. I'd like to get all these (instance)variables in an Array or some Collection so I can iterate through them and set them to some value, not nil.
How can I do this?
I would like to build up on #Uko's answer because there is a more direct way to implement his idea.
The message instSize sent to a Class will answer the number of named instance variables of its instances. This, of course, would include instance variables defined in superclasses.
For instance, RemoteTempVectorNode instSize answers with 17 (wow!). Therefore you could do:
fields := (1 to: anObject class instSize) collect: [:i | anObject instVarAt: i]
and then, change them with:
values withIndexDo: [:v :i | anObject instVarAt: i put: v]
where values is the array of new values you want to inject into the object.
So, why I'm suggesting this instead of instVarNamed:? Because the latter is indirect. If you take a look at its implementation you will see that it has to first find out the name of the i-th ivar by sending instVarIndexFor:ifAbsent: to the object's class. In other words, if you need the ivar names, follow #Uko's suggestion; otherwise don't bring them into the equation because they will only add CPU cycles to your program.
One more thing. As #Sean DeNegris wisely raised in his comment to your question, it would be beneficial if you elaborated a little bit more on why you need such an unusual maneuver.
EDIT:
Now that Pharo has Flexible Object Layouts the mapping between inst var names and the class instSize is no longer valid (in classes that use the new capability.) So, the simpler approach of using just indexes would not work with generality. In fact, under the new "taxonomy" the instSize (number of fields) of an object may be different from the #numberOfInstanceVariables. I guess that the added flexibility has its costs and benefits.
You can send #allInstVarNames to a class (Behavior) to get names of all instance variables defined by it and by superclasses. If you need without superclass variables, you can use #instVarNames
Let's say that var is your variable that you need to work with. Then you can get the collection of instance variable names and iterate them.
You can use #instVarNamed:put: to set instance variable by name, and #instVarNamed: to get the value by name (in case you need).
I think that something like this may help you:
var class allInstVarNames do: [ :instVarName |
var instVarNamed: instVarName put: <yourValue>

How does d3 select array for iteration?

I'm going through this example of a force based layout.
http://bl.ocks.org/sathomas/774d02a21dc1c714def8
The layout is defined as
force = d3.layout.force()
.size([width, height])
.nodes(dataNodes)
.links(dataLinks);
Later in the code though the author iterates through the link array with this call
force.linkStrength(function(link) {
if (link.className === 'red') return 0.1;
return 1;
});
How does the author know that function(link) will iterate over the link array and not the node array?
Because when you write this:
force = d3.layout.force()
.size([width, height])
.nodes(dataNodes)
.links(dataLinks);
You're doing what is called a method chaining. d3.js is known to use them a lot and each method chaining (with this framework at least) returns an object, depending of the method you call. With this particular code, it acts this way:
Set the size of the force object and return the force object
Set the nodes of the force object and return the nodes property of force
Set the links of the nodes object, and return the links property of nodes
So, each method chaining that you add returns something different. At the end of all the method calls, the whole thing will be considered as returning the return value of your last call, and it will assign it to the variable at the left of the = operator. Here, your force variable.
It's exactly as if you wrote:
force = d3.layout.force()
force = force.size([width, height])
force = force.nodes(dataNodes)
force = force.links(dataLinks)
When, later on, you iterate over your force variable, you necessarily iterate over the links array, because that's what you assigned to force.
Tip 1: The tricky part is that here, and generally in d3.js, each method does not return the same objects. Both d3.layout.force() and size() return the actual force object, whereas nodes() returns the nodes object and links() returns the links object. It's cascading method chaining. Each element of the chain is called on the object returned by the previous method call.
Tip 2: you can understand from this that it's not necessarily the best fit to name this variable force.
There's also a cool explanation that goes into further details from Scott Murray.

Kaminari doesn't work If my query is in the model

I have my mongoid query in the model
def self.get_result collection_name, hash_params, page, per_page
self.collection_name = collection_name
#result_pg = self.where(hash_params).page(page).per(per_page)
end
I have it here because my collection names are passed as parameters and I don't have models for all my collection. Instead I have one model and I set its name dynamically based on the request parameter.
My controller code
Collection.get_result params[:state], hash_param, params[:page], params[:per]
My View code
<%= paginate #result_pg %>
When I use the above code I get
undefined method `current_page' for nil:NilClass ERROR
I think the variable is being lost when you call it in the model. Instead, have it return the object back to you like so:
return self.where(hash_params).page(page).per(per_page) // put this in your model
Then, I'm the controller, connect the pieces:
#result_pg = Collection.get_result params[:state], hash_param, params[:page], params[:per]
// in your controller like so
Right now, the controller isn't setting #result_pg, that is why it is nil and you are getting the nil error. Try this and let me know if that worked out for you.
Updated
Ok, as for the reason this happens. When Rails loads a controller, the variables that you declare with a # symbol, like #bologna will be passed to the view and something will be done with them.
On the other hand, you are not technically declaring and instantiating that variable in the controller, you are doing it in the model, as per your source code that you posted above. Declaring the variable there can be done but it isn't useful because the controller has no idea that it even happened. When you tell the Class Collection to perform a method, usually you have that method return something back to you, if you don't, then the variable is lost.
It is similar to me asking someone to go to the store and buy me some groceries, you did the right things, giving the method all the information it needed, passing it the right variables to do its job correctly, but not telling it to actually come back with them, is where the error occurs. In your method, the groceries get purchased at the store and left there. The revised function I wrote for you tells it return with the groceries and put them in the variable #result_pg. The variable is declared in the Controller like it is supposed to be.
So in short, anything you want to have accessible in the view, needs to be declared in the controller. And anytime you want something back from a method, always have it return the information to you.
Also, it isn't entirely necessary to even have the method call to the Class Collection. In a project of my own where I use Kaminari, I just simply do the whole call from in the controller like so:
#notes = current_user.notes.page params[:page]
// My Application has :users that have_many :notes
So you could simplify it that way if you want, but the method that I suggested that fixes it the way you are doing it will work too, however you prefer.
I hope my explanation helped and wan't too long winded.

Using class methods to update properties in an object

After asking my previous question I have come to a situation where I use a couple of object classes stored in each other's properties to retain access to several fields and methods. For example
classdef Class1
properties
Class1Prop % A property accessible from Class1
Class2 % A cell array of class 2 objects
end
methods
% Construct the class with all of its properties
function self = Class1()
end
function Class1Method
self.Class1Prop = ...
end
end
end
I populate an object of Class1 that contains a cell array of Class2. Now I would like to have methods change the values of properties inside of this object. i.e.
Class1{index}.Class2{index}.Class2Method
Will perform some computation and now have that value stored somewhere in that instance of the class.
As stated in the matlab documentation:
"If a function modifies a handle object passed as an input argument,
the modification affects the object referenced by both the original
and copied handles."
To gain the functionality I want, I have to use value classes (with methods that return the class object) so that the value returned by the method call is changed. The value returned can be assigned as well:
Class1{index}.Class2{index} = Class1{index}.Class2{index}.Class2Method
However, ideally
Class1{index}.Class2{index}.Class2Method
would update the Class2 properties. And that is the functionality I want. Is this possible?
Class1{index}.Class2{index} = Class1{index}.Class2{index}.Class2Method
Is the ideal way to address the stated need. It is made possible by using value classes populated by a handle class.

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).