Rails : Benefit of URL path modification vs Namespace? - namespaces

Many people talk about namespace as a way to change the URL that lead to their controller with a prefix (i.e. : /admin/movies instead of /movies)
Changing the URL path
The official doc explains that if we want to change the URL that lead to our controller with a prefix, we just have to change our resource in config/route.rb.
From this :
resources :movies
to this :
resources :movies, :path => "/admin/movies"
Implementing namespace
After a lot of googsearches, I'm wondering why so many people like to have namespace and what are the adavantages of namespace versus just modifying the URL path leading to a specific controller in the router file : myapp/config/route.rb
To implement a namespace, the official doc explains that you need rather the following modifications. You will see that this is a lot of work :
namespace :admin do
resources :movies
end
...and moves your movies_controller.rb controller to app/controllers/admin directory.
But if you follow these instructions, you'll get this error :
*"Expected /var/www/myapp/app/controllers/admin/movies_controller.rb to define Admin::MoviesController"*
*Then you realized that Rails expect this 'alien' writing at the begining of your movies_controller.rb : "Admin::"*
So you change the first line of your movies_controller.rb with :
class admin::MoviesController < ApplicationController
instead of :
class MoviesController < ApplicationController
But you again get another error :
"undefined local variable or method `admin' for main:Object"
Then you realized you forgot Ruby classes MUST be declared with a starting uppercase.
So you add a starting uppercase to your Ruby class :
class Admin::MoviesController < ApplicationController
But you still get an error :
*Missing template admin/movies/index, application/index with {:locale=>[:"fr-CH"], :formats=>[:html], :handlers=>[:erb, :builder, :rxls, :coffee, :haml]}. Searched in: * "/var/www/myapp/app/views"*
What the hell...? Oh did the doc forgot to say as well that controller's corresponding views must as well be moved to an admin directory within the app/view/ ?
So you create a directory and move your views to app/view/admin/
And you still get the same error.
Then you realized you forgot to include the movies folder itself within app/view/admin
So you did it.
You still got an error message :
*undefined method `movies_path' for #<#:0xa817c0c>*
This time you know that routing, controller and view work but you still have to change the pathes on all your views...
Moreover, if you are using the "respond_with" method in your controller file, you have to include the namespace as parameter. This is an example for the index operation :
def index
#movies = Movie.all
respond_with(:admin, #movies)
end
Moreover if you are using declarative_authentification gem (similar as cancan gem), you have to prefix the argument of your permited_to? method with the namespace. For example (with HAML syntax) :
- if permitted_to? :index, :admin_movies
// And prefix as well your path
= link_to 'Show all my movie", admin_movies_path
- if permitted_to? :show, :admin_movies
// And prefix as well your path
= link_to 'Show selected movie", admin_movie_path(#movie)
You realize you were going to create a namespace just for the url path conveniance, so you decide to give up, roll back your modif, and just add the following line in your route.rb file :
resources :movies, :path => "/admin/movies"
Which works immediately like a charm.
As testing purpose I created a new fake project "testapp" including namespace with the generator.
I performed a "rails generate scaffold admin/movie title:string" to check how does the generator handle namespace.
The resulting app/controller/admin/movies_controller.rb is rather different from what we expected it to be.
There are additionnal prefix "Admin::" in front of each instance variable declaration.
Here for instance the index method :
def index
#admin_movies = Admin::Movie.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #admin_movies }
end
end
I guess that this is because the generator as well moved the model movie.rb into app/models/admin directory
Moreover I see that there is another model in app/models : admin.rb
Which contains :
module Admin
def self.table_name_prefix
'admin_'
end
end
I have no idea how this method will be used nor by who ?
I notice as well that my table will be named "admin_movies" in the /db/migrate/2012blabla_create_admin_movies.rb
class CreateAdminMovies < ActiveRecord::Migration
def change
create_table :admin_movies do |t|
t.timestamps
end
end
end
Well, it seems there will have a lot more work (and mess-up) than I expected in order to put in place a namespace...
For what benefit/advantage ???
To use another url path ? This small modification in my controller
would be far more easier : resources :movies, :path =>
"/admin/movies"
To make it possible to use two different controllers using a same name ? Wouldn't it be easier to just put a prefix to my controler name than do all this mess-up ?
Of course some of the above errors could have been avoided by being more vigilant. But you know, we are just human, and when it's late in the evening while coding, maybe you wished this part of rails would have been, let's say, more human adapted...
I really don't understand why people get so excited to have namespace.
I woudld be gratefull, if an expert could explain what am I missing.
Many Thanks

If you have two different controllers with the same name but different functionality then you can use namespaced routes. You don't have to rename the controller for it. Check out "2.6 Controller Namespaces and Routing" at http://guides.rubyonrails.org/routing.html.

The idea is not to have a ton of controllers in one directory. Namespaces help you do that by grouping controllers in directories.
And as far doing all that work, it's a lot easier just to generate the controllers you need using the namespace paths, eg:
rake generate controller "Foo/Bars"
Then rails does all that heavy lifting so you don't have to.

Related

ActiveModelSerializers Polymorphic Json

Been wrestling with trying to get polymorphic serializers working and testing data via rspec. Just upgraded to 0.10+
I found this post, which makes a lot of sense, and does give me a entry into generating the serializations, however, when doing it for polymorphs, I never get the type and id properly named (expecting to see asset_id and asset_type nested)
{:id=>1,
:label=>"Today I feel amazing!",
:position=>0,
:status=>"active",
:media_container_id=>1,
:asset=>
{:id=>4
Test ActiveModel::Serializer classes with Rspec
class MediaSerializer < ApplicationSerializer
attributes :id,
:label,
has_one :asset, polymorphic: true
end
I noticed that the tests dont even seem to properly add the polymorphic identifiers either (ie asset_id, asset_type -- or in the test case imageable_id, imageable_type)
https://github.com/rails-api/active_model_serializers/commit/045fa9bc072a04f5a94d23f3d955e49bdaba74a1#diff-c3565d7d6d40da1b2bf75e13eb8e6afbR36
If I go straight up MediaSerialzer.new(media) I can poke at the .associations, but I cant seem to get them to render as if I was generating a full payload
From the docs
https://github.com/rails-api/active_model_serializers
serializer_options = {}
serializer = SomeSerializer.new(resource, serializer_options)
serializer.attributes
serializer.associations
Im pretty sure Im missing something/doing something wrong - any guidance would be great.
Thanks
It isn't easy to get the effect you are looking for, but it is possible.
You can access the hash generated by the serializer by overriding the associations method.
class MediaSerializer < ApplicationSerializer
attributes :id,
:label,
has_one :asset, polymorphic: true
def associations details
data = super
data[:asset] = relabel_asset(data[:asset])
data
end
def relabel_asset asset
labelled_asset = {}
asset.keys.each do |k|
labelled_asset["asset_#{k}"] = asset[k];
end
labelled_asset
end
end
I learnt alot about ActiveModelSerializer to get the hang of this! I referred to Ryan Bates' podcast on the topic:
http://railscasts.com/episodes/409-active-model-serializers
In there he describes how you can override the attributes method and call super to get access to the hash generated by the serializer. I guessed I could do the same trick for the associations method mentioned in your post. From there it takes a little bit of Ruby to replace all the keys, but, if I have understood correctly what you require, it is technically possible.
Hope that helps!

ActiveRecord undefined method `has_key?' for nil:NilClass error

I have a fairly simple forms-over-data Rails app that calls a remote MySql 5.5 db. Using Rails 3.2.21, Ruby 1.9.3.
One of the pages in the app is throwing the following error:
NoMethodError in GvpController#input
undefined method `has_key?' for nil:NilClass
app/controllers/gvp_controller.rb:9:in `input'
Here is the offending code from the controller:
class GvpController < ApplicationController
def input
# irrelevant stuff
#list = Vendor.gvp_vendor_names.map { |x| x.vendor_name }
# more irrelevant stuff
end
# other irrelevant methods
end
I'm assuming the call to gvp_vendor_names is returning nil.
Here is the Vendor model class:
class Vendor < ActiveRecord::Base
establish_connection :vendor_sql
self.table_name = 'reporting_dw.vp_vendor_mapping'
scope :gvp_vendor_names, -> {
select('reporting_dw.vp_vendor_mapping.vendor_name')}
end
I have searched other posts with this error message and so far haven't found one that seems relevant. I am not overriding the initialize method (one possible cause) and I think the syntax is correct (another).
As an additional wrinkle, I am using vagrant for development, so I thought perhaps I'm not successfully communicating with the database from the vagrant box - maybe an ssh or permissions issue. To test it, I opened an ssh session on the vagrant box, successfully connected with the db via command line ran a select statement and lo and behold, get the full list of results I was expecting. I also tried it with mysql workbench via ssh and had no problems. So, it seems I can communicate remotely with the db, execute queries against it, have the proper permissions etc.
Does anyone have any suggestions as to what the problem might be?
I assume that you haven't any value on your DB tables. That's why the issue arise in controller action block during you call gvp_vendor_names mapped value vendor_name
You should handle this type of case by checking the object value rather than accessing firstclass
GvpController < ApplicationController
def input
# irrelevant stuff
#list = Vendor.gvp_vendor_names.map { |x| x.vendor_name if x.present?}
# more irrelevant stuff
end
# other irrelevant methods
end
In this way you need to compact the nil value. So use this finally if you want to handle the scenario from controller:
class GvpController < ApplicationController
def input
# irrelevant stuff
#list = Vendor.gvp_vendor_names.map { |x| x.vendor_name if x.present?}.compact
# more irrelevant stuff
end
# other irrelevant methods
end
The real problem may just be that I'm a Rails/ActiveRecord n00b. After a little more experimentation, I found the following changes corrected the error.
In the model I added attr_accessible and then used engineersmnky's suggestion of using a method rather than scope, as follows:
class Vendor < ActiveRecord::Base
establish_connection :vendor_sql
attr_accessible :vendor_name
self.table_name = 'reporting_dw.vp_vendor_mapping'
def self.gvp_vendor_names
pluck(:vendor_name).sort
end
end
Then in the controller:
class GvpController < ApplicationController
def input
#irrelevant stuff
#list = Vendor.gvp_vendor_names
#irrelevant stuff
end
end
That fixed it. Thank you everyone for the suggestions!

return array in rails and storage it

I know that it could sound an "easy-question" but I am new in rails and I don't know if I am structuring correctly my app.
My app is "easy" it has to connect a webpage and collect all JPG links and store the links in a database.
In my "model folder" I have defined my functions:
class JPG < ActiveRecord::Base
acts_as_paranoid
validates :title, :link, presence: true
validates :link, uniqueness: true
attr_accessible :tag_list, :id, :title
#acts_as_taggable
def main_web
require 'rubygems'
require 'scrapi'
require 'uri'
Scraper::Base.parser :html_parser
scraper = Scraper.define do
array :items
process "div.mozaique>div", :items => Scraper.define {
process "div.thumb>a", :link => "#href"
}
result :items
end
uri = URI.parse(URI.encode(web))
return scraper.scrape(uri)
end
end
As you can see the function main_web returns an array.
Well my question is "easy". If I want to call the function and to storage the information in the database, how do I have to do it?
I don't want to show the information in the screen I only want to storage the data in the database but from where do I have to do the call? view? or controller?
I suggest you read more about the controller-view model. You need to call and store in the database from the controller.
If main_web returns an array then wouldn't #jpg = #jpg.main_web mean that #jpg is now an array instead of the Active Record model? That would seem to be why it wouldn't save. You might want to add a field to your database that holds the array you generate from main_web. Then instead of returning it you could just call save on your #jpg model.
Also a nice tip would be if you are having issues with models or other things really try typing 'rails console' on command line. You could run your 3 lines of code there to see if they worked. This wouldn't solve your issue but might allow you to play around with some things.
#jpg = JPG.new
#jpg = #jpg.main_web
#jpg.save
Replacing my initial answer after giving it more consideration...
You are doing this:
return scraper.scrape(uri)
which you assign to the #jpg instance. Thus, #jpg is no longer an instance of JPG and cannot be saved as such.
Also, it is not necessary to call and store from the controller. The controller is more a decision maker than anything. It determines state and routes control and information to the desired view or model. The model is where data should be managed. Though, that may be a matter of debate and is my opinion.
Recommended reading:
http://sirupsen.com/what-I-wish-a-ruby-programmer-had-told-me-one-year-ago/

Ember.js with Rails accepts_nested_attributes_for and polymorphic has_many/belongs_to

On the Rails API side I have the following 2 models:
class Grower < ActiveRecord::Base
has_many :addresses, as: :addressable
accepts_nested_attributes_for :addresses
end
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
end
as well as a Growers controller which returns and can create/update Growers with embedded Addresses attributes. I also have an Addresses controller with proper routing so that Addresses can be viewed/created/updated for a specific Grower. The latter is more of an "in-case", and I'm not sure I'll be needing to return/update/create addresses as a separate payload.
I am starting to try and piece together an Ember app that would allow me to view/edit/create a Grower at the same time as its Address(es). Can anyone point me to an existing real or sample app that does this? I will be posting my code as I go along, but I already have an idea of some areas where I will be running into trouble:
Rails returns/expects nested params named addresses_attributes. Ember, I am sure, doesn't use that convention. What's the best approach of resolving this?
Because of the polymorphic association (objects other than Grower can be addressable), on the API/Address side, to get the correct belongs_to object, Rails uses addressable_id in conjunction with addressable_type. In this example the addressable_type would be "Grower", and the addressable_id would be the grower_id value. How would one go about translating that on the Ember side?
UPDATE:
I got it to work at least a couple different ways. My preferred solution, at least for this particular case, is in the answer section.
Here is a sample of code based #yuяi's strategy that worked well for me:
App.Post = DS.Model.extend
comments: DS.hasMany('comment')
App.PostSerializer = DS.ActiveModelSerializer.extend( DS.EmbeddedRecordsMixin,
attrs:
comments: {embedded: 'always'}
keyForAttribute: (attr) ->
if attr == "comments"
"comments_attributes"
else
#_super(attr)
)
This solution worked well with Ember 1.6.1 and Ember Data 1.0.0-beta.8.2a68c63a.
I found a couple ways to get it done, but the final approach doesn't require any changes on the Rails/API side.
On the client (Ember) side:
I added the addresses hasMany property to the App.Grower model. I also mapped it on the RESTAdapter to what's expected from the API, by setting the key for addresses to addresses_attributes.
I added the grower (for now - will change to addressable once I have other addressable models) belongsTo property on App.Address. It's not really required for what I'm doing, but it might be useful in the future.
I set the addresses on the RESTAdapter to be embedded: 'always'.
On the App.GrowersEditController, I just do a model.save (transaction.commit), and the child addresses are automatically saved via the API call.
On the App.GrowersAddController, I use the App.Address.createRecord and App.Grower.createRecord methods using the user-entered Grower and Address data. Then I use the pushObject method to append the Address to the Grower, and then call a save on the Grower (commit on transaction). Again, the address data gets submitted and saved automatically.

can't update the attribute with ActiveRecord

I want to swap the content in answers table with ActiveRecord.
code 1:
Archieve::Answer.find_each do |answer|
str = answer.content
dosomething() #change the value
answer.update_attribute(:content,str)
end
But It doesn't change the value of content.
code 2:
Archieve::Answer.find_each do |answer|
str = answer.content
dosomething() #change the value
answer.reload
answer.update_attributes(
:content => str
)
end
Before update the :content attributes, I reload the record every time.
It can indeed change the the value.
Why?
What's the difference between code 1 & code 2?
Source Code
###1 Post Debug Message:
Updated Post:
Changed?: false
valid?: true
errors: #<ActiveModel::Errors:0xa687568>
errors: #<ActiveModel::Errors:0xa687568 #base=#<Archieve::Answer id: 9997190932758339, user_id: 4163690810052834, question_id: 3393286738785869, content: "狗狗生病,好可怜呀,", is_correct: false, votes_count: 0, comments_count: 0, created_at: "2011-11-06 18:38:53", updated_at: "2011-11-06 18:38:53">, #messages={}>
possible ActiveRecord 3.1.1 bug
The OP mentioned to me that he uses require "active_record" in a stand alone script (not using rails runner).
There is no separate Rails application for his task, he just uses a script. This is not necessarily bad, and has worked in earlier ActiveRecord versions, e.g. 2.x AFAIK -- maybe this is a regression in Rails 3.1 due to a new dependency?
# the OP's require statements:
require 'rubygems'
require 'logger'
require 'yaml'
require 'uuidtools'
require 'active_record'
complete code here: https://raw.github.com/Zhengquan/Swap_Chars/master/lib/orm.rb
maybe a dependency is missing, or problem with AR 3.1.1 when initialized stand alone?
It could be a bug actually
It could be that update_attribute() triggers a bug in the dirty-tracking of attributes, which then incorrectly assumes that the object has not changed, and as a result it will not be persisted, although the implementation of update_attribute() calls save() (see code fragment below).
I've seen something like this with an older version of Mongoid -- could be that there is a similar hidden bug in your ActiveRecord version for update_attribute()
In the Rails Console monkey-patch update_attribute like this:
class ActiveRecord::Base
def update_attribute(name, value) # make sure you use the exact code of your Rails Version here
send(name.to_s + '=', value)
puts "Changed?: #{changed?}" # this produced false in the OP's scenario
puts "valid?: #{valid?}"
puts "errors: #{errors.inspect}"
save
end
end
then try to run your Code 1 again...
you shouldn't see "Changed?: false".. if it returns false, although you changed the attribute, then there is a bug in your ActiveRecord version and you should report it.
Code 1:
NOTE: check the definition of update_attribute() (singular) here:
(please read the fine-print regarding validations -- it doesn't sound like a good idea to use that method)
http://ar.rubyonrails.org/classes/ActiveRecord/Base.html#M000400
See also:
Rails: update_attribute vs update_attributes
The source code for update_attribute() looks like this:
2260: def update_attribute(name, value)
2261: send(name.to_s + '=', value)
2262: save
2263: end
it could fail if there is a bug with the dirty-tracking of attributes...
Code 2:
The second code looks correct.
There are a couple of things to also consider:
1) which attributes did you define as accessible, via attr_accessible ?
e.g. only accessible attributes will be updated via update_attributes()
http://apidock.com/rails/ActiveRecord/Base/update_attributes
2) which validations do you use?
are you sure the validations pass for the record when you call update_attribute?
See also:
http://guides.rubyonrails.org/active_record_querying.html
http://m.onkey.org/active-record-query-interface
http://api.rubyonrails.org/classes/ActiveRecord/Base.html