Rails 4, creating model entries by parsing xml file. Need current_user - ruby-on-rails

Fairly new with rails and am digging with a new web app. I'm parsing an XML file uploaded by the user. The parsing is done with a before_save call in the upload.rb model file.
In the xml parsing function (parse_xml), my intention is to create a new model for the XML children elements and save all the attributes.
The problem I am facing is this:
As I understand, current_user is a controller helper function from devise and is not available in the models. The current model I have (Items Model containing the XML child elements) is tagged against a user_id (The current_user). The Items model does not have controllers at the moment and there is no way I can think of passing in the current_user's ID attribute into the Uploads model.
Again, to my understanding, the Uploads model works on the parse_xml function before actually running the #upload.save function in the uploads_controller.rb.
I'm not sure how to approach this problem. While I know Thread.current[:user] is suggested by a lot of people, as a learner, i prefer getting the right approach in my head rather than have a #facepalm moment when someone looks at my code :-)
Update:
Since Items records require an upload_id value, the records cannot be created until the Parent Upload record is created. Is this the reason why I also get an uninitialized constant Upload::Item error during the before_save execution in Upload#create route?
I'm not sure where things are going wrong!

Related

mongoid splits nested attributes in two hashes?

I've got a model List with nested attributes from another one, Article. What bugs me is I have Ruby request params having my first nested attribute in Article hash, and all the others(2nd,3rd article data, etc) in article_parameters.
I follow the standard mongoid tutorial here.
I have #list.articles.build in create method of List controller.
Can I influence what goes into build method? I've tried to call build(all_the_correct_hashes) but it does not seem to fix the issue.
So, in the nutshell I have 2 questions.
why articles gets only the first nested attribute, 2nd and next go to article_parameters hash?
why list.articles.build only creates _id in my Mongo document, but does not populate it with other fields?
I'll write up my solution, since I see people upvoting my question.
Frankly, I did not find any answer as to why such behaviour occurs.
The solution is a bit hackish.
In your save controller, call List.article.build(id => params[:id], someotherstuff => params[:someotherstuff])
Everything you pass to build in this case will populate article_parameters hash.
id and sometherstuff are fields of my Article model.
I feel this should be done implicitly by Mongoid, but it just does not work. Their development does not see anything wrong with it, either.

Model Accepts Nesting Attributes without declaring that it accepts them

everyone. So, I'm working on a basic Rails 4 application for practice, and I have a model for FriendCircle and a model for FriendCircleMembership. (the FriendCircleMembership's corresponding table is basically a join table).
In the console, I'm able to create a new FriendCircle object while passing in :friend_circle_memberships_attributes. This successfully inserts a new FriendCircle row into my table as well as inserting the proper rows into the FriendCircleMembership table.
The WEIRD thing is that, even if i comment out that the FriendCircle accepts_nested_attributes for :friend_circle_memberships, it still works. Is this because i am whitelisting it as a permission in the controller?
The other issue is that, even though i can successfully make the nested objects via the rails console, when i try making it through my html form it says my friend_circle_memberships_attributes is an unpermitted parameter. Not sure why this is happening. I check the incoming parameters and they look fine.
any help would be SWEEEEET. thanks.
I determined what the error was: One of my controllers for a nested attributes was validating the presence of the id of the controller it was nested under.
I'm assuming the validation occurs before an id is created, which makes sense, but im not 100%. So i just took out the validator and things worked.

can find_or_create_by_name update?

when using find_or_create_by_name in rails, if the table is found and you pass in parameters for other attributes, will it update the table? for example:
College.find_or_create_by_name(name: 'University of Pittsburgh', calendar: 'semester')
Say the University of Pittsburgh table has already been created, but the calendar attribute is nil. Will this code update the calendar attribute to make it 'semester'?
Some context... I'm making a website with a bunch of pages for different colleges. Part of the website involves listing a bunch of data for the college. Right now I'm writing my seed file, but I anticipate having to change it. I'd like to have hundreds of schools on the website, and for each school, there are going to be hundreds of different pieces of data. I was thinking that having the seed file and using find_or_create_by_name would be a good way to do this, but if you have a better way, please let me know.
That code won't update the record if it already exists. I would suggest:
#college = College.find_or_initialize_by_name("Robot House!!")
#college.attributes = {
reputation: "partyhouse",
occupants: "robots"
}
#college.save!
You could wrap this in a method if you needed to.
find_or_create_by_ does exactly what it says: it either creates a new item (create saves to database) or finds the existing one, meaning reads it from the database.
It returns false on validation errors when creating an object
So to save changes you use the normal update methods:
if #college= College.find_or_create_by_name(given_attributes) &&
#college.update_attributes(given_attributes)
else
# handle validation errors
end
It won't hit the database twice, because update_attributes does'n apply any changes to newly created objects (but possible changes to existing ones)
To write it more explicit:
#college= College.find_or_create_by_name(given_attributes)
if #college.present?
if #college.update_attributes(given_attributes)
# do your success stuff
else
# handle update validation errors
end
else
# handle find_or_create errors
end

What kind of information can the Model access in Rails?

In Ryan Bates's first episode on complex forms, he adds the following to a model:
# models/project.rb
has_many :tasks
def task_attributes=(task_attributes)
task_attributes.each do |attributes|
tasks.build(attributes)
end
end
I've never thought about this before, but how does the Project model know what "tasks" of which Project instance? Does that come from the has_many association? Is it like, when the project is running and I'm viewing a Project, that's the "active" object so project.rb knows which Project object we're referring to, so it knows that tasks is really some_current_project.tasks? (I'm obviously grasping at straws here.)
Also, if someone would point me to some reference that explains other questions like this one, I'd really appreciate it.
I hope my question is clear. Please ask for more clarification in comments if needed.
Please note: I know that Active Record handles CRUD actions and that objects correspond to rows in tables, etc. Those are just descriptions of what Active Record is. I'm looking for how it works when the project is running. I also now the constructs MVC, but I can't seem to find a detailed explanation of what information is sent where with respect to Rails.
(Not sure I fully understood your question, feel free to let me know if that's the case.)
A rails model is basically a ruby class that is persisted to a database. So it acts like a normal ruby object for the most part, with some database magic mixed in.
You tell rails which project instance to load (e.g. by providing an id), and it loads the data from the database.
Then, when you call project.tasks is when the magic happens: the Project model has no tasks method, so it will trigger ruby's method_missing method. This will then load the associated records into model instances and provide access to them via a rails object.
Since a project has many tasks, rails knows it should look into the tasks database and load the rows where project_id is equal to the project model's id attribute.
In short, ruby meta-programming and monkey patching possibilities make much of rails' magic possible.
(Edit for question on routing.)
When you want to edit project number 13, you go to a URL that looks something like www.mysite.com/projects/13/edit. If you look at routes.rb in your config directory, you'll see (in Rails3) resources :projects what Rails does is set up all sorts of paths for you. Behind the magic, the edit path looks like
get '/projects/:id/edit' => 'projects#edit'
This basically says "when a user wants to see www.mysite.com/projects/13/edit, send him to the edit action in the projects controller and set the id parameter to the value that's in that place.
Then in your controller, you'll load the appropriate project with
#project = Project.find(params[:id])
In a similar way, you could do this (this is an dumb example):
In routes.rb, put
get '/projects/:id/edit_name/:name' => 'projects#edit'
And then in you controller
#project = Project.find(params[:id])
#project.name = params[:name]
So rails basically uses magic to assign values in the URL to params you can work with in your controller. You can read more about routing here: http://guides.rubyonrails.org/routing.html

Calling ActiveRecord's #relationship_ids = [1,2,3] saves immediately. Any workarounds?

I've come across an oddity in ActiveRecord's #relationship_ids method (that's added automatically when you declare 'has_many'), which saves immediately for existing records, which is causing me some issues, and I wonder if anyone had any useful advice.
I'm running Rails 2.3.5.
Consider this simple scenario, where an article has_many tags, say:
a = Article.first
a.name = "New Name" # No save yet
a.author_id = 1 # No save yet
a.tag_ids = [1,2,3] # These changes are saved to the database
# immediately, even if I don't subsequently
# call 'a.save'
This seems surprising to me. It's specifically causing problems whilst trying to build a preview facility - I want to update a bunch of attributes and then preview the article without saving it - but in this instance the tag changes do get saved, even though no other fields do.
(Of possible relevance is that if 'a' is a new article, rather than an existing one, things behave as I'd expect - nothing is saved until I call 'a.save')
I have a fairly nasty workaround - I can override the tag_ids= method in my model to instead populate an instance variable, and actually save the related models in a before_save callback.
But I'd love to know of a simpler way than me having to do this for every model with a has_many relationship I'd like to create a preview facility for.
Does anyone have any fixes/workarounds/general advice? Thanks!
There's a reason things are this way. It's called foreign keys. In a has many relationship, the information that links to the model that has many is stored outside of that model as a foreign key.
As in Articles, has many tags. The information that links a tag to an article is stored either in the tags table or in a join table. When you call save on an article you're only saving the article.
Active record modifies those other records immediately. Except in the case where you're working with a new article that hasn't been saved yet. Rails will delay creating/updating the associated records if it doesn't know which id to place in the foreign key.
However, if you're modifying existing records, the solution you've decided on is really all that you can do. There's an even uglier hack using accepts_nested_attributes_for, but it's really not worth the effort.
If you're looking to add this behaviour to many models but not all models, you might want to consider writing a simple plugin to redefine the assigment the method you need and add the call back in a single class method call. Have a look at the source of something like acts_as_audited to see how it's done.
If you're looking to add this behaviour to all models, you can probably write a wrapper for has_many to do that.

Resources