Is this a case for inheritance? - ruby-on-rails-3

I have Rails models User, ReadingList and SessionReadingList. A user has many reading lists. A SessionReadingList is a special type of reading list for before a user has registered, stored in the session.
In my ReadingListsController every action is of the form:
def show
if current_user
#load user's reading lists
else
#load session reading list from session
end
end
I'm wondering whether I'd be better off subclassing ReadingListsController so I have e.g. SessionReadingListsController and UserReadingListsController. I don't know how I'd handle the routing then though.
So, is subclassing the solution? If so, do I redirect from the ReadingListsController depending on current_user? Or is there a better way?

You can create a custom route matcher that uses the appropriate controller.
class LoggedInConstraint < Struct.new(:value)
def matches?(request)
request.cookies.key?("user_token") == value
end
end
match 'reading-list' :to => "reading_list#index", :constraints => LoggedInConstraint.new(true)
match 'reading-list' :to => "session_reading_list#index", :constraints => LoggedInConstraint.new(true)

Related

Rails force all translations

I am using globalize3 with rails_admin thanks to this gist. What bugs me, is that the user can add as many translations as he wants.
Moreover, he isn't forced to translate the content in every single locale (as in I18n.available_locales). I'd like that. How can you tackle such a situation?
Models (shortened):
class Project < ActiveRecord::Base
has_many :project_translations, :dependent => :destroy, :inverse_of => :project
accepts_nested_attributes_for :project_translations, :allow_destroy => true
class ProjectTranslation < ActiveRecord::Base
belongs_to :project
I ended up using Active Admin plus activeadmin-globalize3 instead. Much easier.
It bugged me too, so I created custom field type that doesn't allow it.
The main class:
module RailsAdmin
module Config
module Fields
module Types
class GlobalizeTabs < RailsAdmin::Config::Fields::Association
RailsAdmin::Config::Fields::Types::register(:globalize_tabs, self)
register_instance_option :partial do
:form_globalize_tabs
end
def method_name
"#{super}_attributes".to_sym
end
# Reader for validation errors of the bound object
def errors
bindings[:object].errors[name]
end
def available_locales
I18n.available_locales
end
def current_locale
I18n.locale
end
# Returns array of Translation objects
# It gets existing or creates new empty translation for every locale
# It's used in fields_for method in partial
def translations
translated_locales = #bindings[:object].translated_locales
available_locales.collect do |locale|
translated_locales.include?(locale) ? #bindings[:object].translation_for(locale) : #bindings[:object].translations.new({ locale: locale })
end
end
end
end
end
end
end
It inherits from RailsAdmin::Config::Fields::Association class, because it uses very similar to _form_nested_many partial (that's used in has_many type).
The partial:
.controls
= form.errors_for(field)
%ul.nav.nav-tabs{ :style => 'margin-top:5px' }
- field.available_locales.each do |locale|
%li{ class: ( 'active' if locale == field.current_locale ) }
%a{ href: "##{locale}", data: { toggle: "tab" } }= locale
.tab-content
= form.fields_for field.name, field.translations, wrapper: false do |nested_form|
.fields.tab-pane{ id: nested_form.object.locale, class: ( 'active' if nested_form.object.locale == field.current_locale ) }
= nested_form.generate({:action => :nested, :model_config => field.associated_model_config, :nested_in => field.name })
= form.help_for(field)
It uses field.translations method from the custom field class, that returns an array of Translation objects.
Every Translation object corresponds to available locale, and it's either an existing object from the database (if translation already exists) or new empty translation object.
E.g.
You've got this available locales:
I18n.available_locales = [:en, :cz, :ru]
You have Page model which includes some translated fields.
Also, you have an object of the class Page (a row in the database), that has translations for :en and :cz locales, but lacks one for the :ru.
So, field.translations method inside _form_globalize_tabs partial returns an array that contains:
2 existing translations for :en and :cz and 1 just initialized translation for :ru.
In the partial I'm passing this array to the fields_for helper method from nested_form gem, that returns 3 fieldsets for every translation object.
You can use this gem, if you don't want to mess with the code yourself: https://github.com/scarfaceDeb/rails_admin_globalize_field

Rails Routing: One controller. One model with type. Multiple routes

I have one model named Factors, which has two types: ['personal', 'advisor']
I want to have one controller FactorsController that has all the same actions for both types of Factors, but only ever uses one type. The type that it uses is based on the route used to get there. For example,
/personal would route to factors#index and populate #factors with Factor.personal
/advisors would route to factors#index and populate #factors with Factor.advisors
How would I go about setting this up?
You can add to the routes
type_regexp = Regexp.new([:personal, :advisor].join("|"))
resources :factors, path: ':type', constraints: { type: type_regexp }
and you will be able to user params[:type] in the controllers, that gives you flexibility in case you wanna changes the routes in the future.
This also gives you the ability to use factors_path(type: :personal) in the views.
You can add this to your routes:
resources :factors, :path => :personal
resources :factors, :path => :advisor
This will then have both /personal and /advisor. You'll then want to have factors#index determine which path was used (you could use request.url) and populate #factors accordingly.
I would create three controllers:
class PersonalController < FactorController
def factor
Factor.personal
end
end
class AdvisorController < FactorController
def factor
Factor.advisors
end
end
class FactorController < ApplicationController
#all the shared stuff here, using the factor method from each in your methods
end
and then the routes would be:
route '/personal' => PersonalController#index
route '/advisors' => AdvisorController#index

Inherited_resources build resource as role

As an example:
def create
resource = build_resource
resource.assign_attributes(params[resource_instance_name], as: :admin)
create! do |format|
format.js {...}
end
end
The problem with above is that attributes are not being assigned with as: :admin, they are being assigned without any check and so this method is not having any effect. Is it the create! method? Attributes are being assigned to this resource elsewhere and I can't find out where it is. Appreciate any insight.
Found my answer here: https://github.com/josevalim/inherited_resources/pull/153. Had to override as_role and role_given? --
def as_role
{ as: current_user.highest_role }
end
def role_given?
true
end
This will then always apply roles to attributes for either the controller it is defined in, or all resources if you inherit from a master resources controller like I do.

Calling ActiveRecord::Create() from within rails user model not associating new object with user

In my rails user model, I am trying to write a method which will return a list for the current time frame, and in the absence of a list for that time frame, create one which is associated with the user and then return it:
class User < ActiveRecord::Base
def todaysList
today = Time.new
if self.lists.where(:date => today.to_date)
return self.lists.where(:date => today.to_date).first #Get the object, not the ActiveRecord::Relation
else
self.lists.create!(:date => today.to_date) #Make the list, return it!
end
end
My question is, why is it that when I call self.lists.create!(:foo => 'bar'), the user association is not populated?
I've decided to get around this a more sloppy way, by explicitly assigning the user in the create! call, as such:
self.lists.create!( :date => today.to_date, :User_ID = self.id)
however this solution just doesn't seem right.
Thanks in advance and apologies as always for stupid, redundant or badly worded questions.
I would do something like this:
def todays_list
lists.find_or_create_by_date(Date.today)
end
The method name change is just preference.

How Modify route standard Rails3?

I am using Rails3 and I would like make one request for get one parameter token to my controller different of ID to my action "confirms "in my controller,can someone help me?
I believe is a problem in my route.
such as:
http://0.0.0.0:3000/emails/1QWD3DF2Cd/confirms or http://0.0.0.0:3000/emails/confirms/1QWD3DF2Cd
`
class EmailsController < ApplicationController
def confirms
#email = Email.find(params[:token])
#email.confirmed # other method in model emails which mark as enable
end
end
`
You can do this
Add this to your routing.rb file.
match '/emails/confirms/:token' => 'emails#confirms', :as => 'confirms_emails'