Rails: Dynamically build array from inherited controllers method - ruby-on-rails-3

I'm building an CMS as a subset of gems, all inheriting from the Base gem. Each gem provides different functionality, i.e. blog, pages, events, etc. I want to setup my Base gem so that it can dynamically build a Navigation based on the installed gems. I don't want to automatically do this by finding all the controllers/routes, I'd rather have each gem identify the navigation items an Array of Hashes, i.e.:
# In my Admin controller (from which all other gem controllers inherit)
#admin_navigation = [{controller: "users", label: "Users", sort_order: 10}, {controller: "settings", label: "Settings", sort_order: 11}]
# In my Pages gem
#admin_navigation += [{controller: "pages", label: "Pages", sort_order: 3}, {controller: "page_templates", label: "Pagetemplates", sort_order: 8}]
# In my blog gem
#admin_navigation += [{controller: "posts", label: "Blog", sort_order: 4}, {controller: "tags", label: "Tags", sort_order: 5}]
Basically I want my #admin_navigation variable to include all of the navigation items from each inherited controller. Is there a way for a controller to call a method from each inherited controller?
Is there a better way to accomplish this? In the end, I just need an array of these navigation item hashes to build my menu. Thanks in advance!

You could use the ruby inherited method for classes. So within your AdminController, you would
def self.inherited(subclass)
#admin_navigation ||= []
#admin_navigation += subclass.admin_navigation
end
Assuming that #admin_navigation is exposed as a class method on your inheriting controllers. You would also have to make sure that AdminController is defined first and after that all controllers are truely loaded afterwards, so that they trigger the callback.

Related

Ruby on Rails , Gem Devise Customization

Hi i am currently working on my Ruby on Rails project and i wanted to customize the devise gem but my problem is that i cant seem to find some tutorials on how to customize devise more specifically customizing the login, parameters.
I already customized the login and the view of the device , but my problem is it doesn't seem to create the username , firstname , lastnamge , age , etc.
and it only gets the email and password etc. the basic stuff
i know the documentation is great and all specially because it is complete but my problem is that i find it a bit hard to follow specially when i cant see the video or how can i customize what i can put inside in the devise if anybody can help me with this , or any information on some tutorials with actually doing what i specified above that would be great and will really be appreciated , and please i am a bit new on RoR so i find it a bit difficult in just reading the text so i need some vdeo and thanks!
simplybel#simplybel:~/projects/gamification$ rails c
Loading development environment (Rails 4.1.6)
2.1.2 :001 > User.all
User Load (0.8ms) SELECT "users".* FROM "users"
=> #<ActiveRecord::Relation [#<User id: 7, first_name: nil, last_name: nil, profile_name: nil, email: "pat#yahoo.com", encrypted_password: "$2a$10$xg.HMU2JljnVLls3IX7Go.IfJVLGYSSRePjtDaYS4nE...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2014-12-01 23:50:17", last_sign_in_at: "2014-12-01 23:50:17", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2014-12-01 23:50:17", updated_at: "2014-12-01 23:50:17">]>
2.1.2 :002 >
this is the what the rails console returned after taking your advice, here is the code that you can clone
https://github.com/sanchez900/gamification.git
that is the code before I did the changes like the
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
end
end
i also did this
rails generate devise:views
also did the controllers
rails generate devise:controllers users
did the routes
devise_for :users, controllers: { sessions: "users/sessions" }
some other info might help also the project clone / link is before i did the modifications
If you had setup your view correctly to submit all the addition parameters, then you need to do as instructed in the docs https://github.com/plataformatec/devise#strong-parameters.
You need to update your permitted parameters on your sign up form to accept the new parameters you need. To do this, you can set a before filter to configure the permitted params for your devise controllers. Try something like this in your application controller.
before_action :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:first_name, :last_name, :profile_name) }
end
you need to explicitly write it as above (or use .push if you prefer)

creating a custom path-helper while using FreindlyID

we're trying to get our site to have less scrapeable AND more readable urls
so e.g.
www.loomio.org/discussions/3122
becomes
www.loomio.org/d/3saA4ds9/lets-go-to-the-moon
we only really want the human-readable slug on show-links, so www.loomio.org/d/3saA4ds9/edit should be the url for editing that discussion
the solution so far follows the top answer here:
Ruby on Rails: How to override the 'show' route of a resource?
modify routes.rb:
get '/d/:id/:slug', to: 'discussions#show', as: :discussion
resources :discussions, path: 'd', except: [:edit, :show] do
get :activity_counts, on: :collection
member do
post :update_description
post :add_comment
post :show_description_history
get :new_proposal
post :edit_title
put :move
end
end
install gem FriendlyID; make and populated a :key column on Discussion table; add the following to discussion.rb (model):
KEY_LENGTH = 10
extend FriendlyId
friendly_id :key
write a custom path helper for group_path. in groups_helper.rb:
def group_url(group, options={})
url_for(options.merge(:controller => 'groups', :action => 'show',
:id => group.key, :slug => group.full_name.parameterize))
end
def group_path(group, options={})
group_url(group, options.merge(:only_path => true))
end
rake routes produces:
group GET /g/:id/:slug(.:format) groups#show
and while calling group_path(group) seems to work in some cases, I'm also seeing strange unrelated urls get generated, like :
http://loomio.org/group_requests/TegFOIx4DB/start_new_group?action=show&controller=groups%2Fgroups&slug=19-tory
in console, I'm also getting errors such as:
[5] pry(main)> g = Group.last
[6] pry(main)> app.group_path(g)
ActionController::RoutingError: No route matches {:controller=>"groups", :action=>"show", :id=>#<Group id: 2811, name: "Sylvester Buckridge", created_at: "2013-12-10 06:25:42", updated_at: "2013-12-10 06:25:42", privacy: "public", members_invitable_by: "members", parent_id: nil, email_new_motion: true, hide_members: false, beta_features: false, description: "A description for this group", memberships_count: 1, archived_at: nil, max_size: 300, cannot_contribute: false, distribution_metric: nil, sectors: nil, other_sector: nil, discussions_count: 0, motions_count: 0, country_name: nil, setup_completed_at: "2013-12-10 05:25:01", next_steps_completed: false, full_name: "Sylvester Buckridge", payment_plan: "undetermined", viewable_by_parent_members: false, key: "rkdlTytOin">}
from /home/s01us/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.16/lib/action_dispatch/routing/route_set.rb:540:in `raise_routing_error'
I've tried putting the group_path and grop_url methods in ApplicationController and ApplicationHelper to no avail.
calling
group_path( group.key, group.fullname.parameterize )
works, but would ideally like to be able to only have to call e.g.
group_path(#group)
as far as i understand the problem, you could use the good old hack with defining the to_param method on your model
class Group < ActiveRecord::Base
def to_param
"#{id}-#{slug}"
end
end
The beauty of this solution is that you won't need to do anything else. Rails will automatically use the to_param method as your record ID when it generates an URL from a record. You can do anything
redirect_to group_path(#group)
redirect_to #grup
# etc
and your Group.find should eat it as it is 123-smth-smth, usually it is smart enough to extract the integer part of the id

Rails 3 nested resources - form partial only works for either new or edit, not both

I have nested resources, and I'm trying to create form partials for each individual resource to use for the new and edit action of each.
routes.rb
resources :accounts, shallow: true, :except => [ :destroy ] do
resources :service, :except => [ :destroy ]
end
If I use the following in the form partial, the edit form renders correctly and saves updates correctly, but the new form fails to render with the error undefined method services_path
/app/views/services/_service_form.html.erb
<%= simple_form_for #service do |f| %>
If I use the following in the form partial, the new form renders correctly and saves the object correctly, but the edit form fails to render with the error undefined method account_service_path
/app/views/services/_service_form.html.erb
<%= simple_form_for [#account, #service] do |f| %>
I've tried adding url: service_path(#service) to both versions of the form block declaration as shown, but it doesn't solve either problem.
There's bound to be something obvious I'm missing with getting this working, I'm sure I've had nested resources working correctly with a single form partial in the past, I just can't see what's different this time.
Change your code to following
change :service to :services
Given your Account Model
has_many :services
and Service Model have
belongs_to : account
resources :accounts, shallow: true, :except => [ :destroy ] do
resources :services, :except => [ :destroy ]
end

Rails 3: How can I make a route helper that generates different routes depending on an object property or method?

I'm making an app in Ruby on Rails 3.1.3. I have different types of users (i.e. admin, operator, advertiser, etc...), and each has a different main (or home) page. I want to make a route helper that will give me the respective route for the home page of the current logged in user by using something like home_path. This is mainly for redirecting after certain actions (I want to redirect back to the respective home pages depending on the type of user).
I already have some methods available such as current_user (returns the current logged in user), current_user.admin? (returns true if the current logged in user is admin), current_user.operator?, etc.
Right now I'm using a helper method to do this, but it doesn't seem like a very Rails way to do it. The code follows anyway:
def home_path(params = {})
user = current_user
case user
when user.admin?
params = {:controller => 'administrators', :action => 'index'}.merge(params)
when user.advertiser?
params = {:controller => 'advertisers', :action => 'show', :id => user.advertiser_id}.merge(params)
when user.operator?
params = {:controller => 'callcenter', :action => 'index'}.merge(params)
else
params = {:controller => 'posts', :action => 'home'}.merge(params)
end
url_for(params)
end
I figure this should be done with constrained routes, but I still don't get how it could be done to depend on the .admin?, .operator?, etc. methods. Any help on this would be greatly appreciated.
Using a helper method is fine for this. It should probably end up in your controller, rather than a view helper, though, which gives it access to the current_user. With some cleanup, you can arrive at something that ain't half bad with the same idea you have now.
module DefaultHomeHelper
DEFAULT_PARAMS = { controller: :posts, action: :home }.freeze
ROLE_SPECIFIC_PARAMS = {
admin: { controller: :administrators, action: :index },
advertiser: { controller: :advertisers, action: :show },
operator: { controller: :callcenter, :action: :index }
}.freeze
def home_path(params = {})
url_for params.reverse_merge(ROLE_SPECIFIC_PARAMS[current_user.role] || DEFAULT_PARAMS)
end
end
I've made the assumption you can be more direct and ask your User object to just tell you its role instead of guessing one after the other. You will almost certainly need to tweak the code to accomodate whatever you're calling this on your user. I've also used the newer hash syntax, but if you're running or accommodating Ruby < 1.9 you will need to update. I've used symbols for the actions and controller names, too, because I like referring to objects and methods with symbols instead of strings (and controllers and actions are objects and methods).
You could do a simple include DefaultHomeHelper in your ApplicationController to use this. You can also make it available to your views with helper_method :home_path.

Paper_trail with accepts_nested_attributes_for

I have a rails app that has articles that and the user can add links and reviews as nested attributes.
I saw in the paper_trail https://github.com/airblade/paper_trail/ documentation that this is not covered by that gem. How would I go about setting up undo functionality so that nested attributes or has_many associations are restored/updated when a user clicks undo?
I think if you hook in a "destroy" post to the undo button it will at least remove the links if they click undo. Basically you pass a hash with the special _destroy key it will remove the nested model records.
From Rails 3 docs here:
class Member < ActiveRecord::Base
has_one :avatar
accepts_nested_attributes_for :avatar, :allow_destroy => true
end
Now, when you add the _destroy key to the attributes hash, with a value that evaluates to true, you will destroy the associated model:
member.avatar_attributes = { :id => '2', :_destroy => '1' }
member.avatar.marked_for_destruction? # => true
member.save
member.reload.avatar # => nil