I have a model that needs editing that is associated with the current user called BillingProfile. How can I add a menu item that links to the edit page of the current user's BillingProfile? I don't want or need an index page for the BillingProfile as a user can only edit their own.
class User
has_one :billing_profile
end
You can use Cancan to manage the ability to allow a User to edit his own Billing Profile.
ability.rb
...
cannot :edit_billing_profile, User do |u|
u.user_id != user.id
end
...
admin/users.rb
ActiveAdmin.register User do
action_item :only => :show do
link_to "Edit BP", edit_bp_path(user.id) if can? :edit_billing_profile, user
end
end
Or you can try something like this:
ActiveAdmin.register User do
form do |f|
f.inputs "User" do
f.input :name
end
f.inputs "Billing Profile" do
f.has_one :billing_profile do |bp|
w.input :address if can? :edit_billing_profile, bp.user
end
end
f.buttons
end
end
I have not test it, but I did something similar on a project.
This may help you-
Adding custom links:
ActiveAdmin.register User, :name_space => :example_namespace do
controller do
private
def current_menu
item = ActiveAdmin::MenuItem.new :label => "Link Name", :url => 'http://google.com'
ActiveAdmin.application.namespaces[:example_namespace].menu.add(item)
ActiveAdmin.application.namespaces[:example_namespace].menu
end
end
end
I basically created a new ActiveAdmin::MenuItem and add it to the current ActiveAdmin menu with the namespace example_namespace and return the menu in the end of the current_menu method. Note: current_menu is a method expected by ActiveAdmin so don't change the name of it. You can add as many items you like and each of these items will be converted to a link on your navigation header. Note this works for ActiveAdmin version > 0.4.3 so you might need to do your own digging if you want to do it for version <= 0.4.3.
I have a LinkHelper defined which has the following two methods:
#This will return an edit link for the specified object instance
def edit_path_for_object_instance(object_instance)
model_name = object_instance.class.to_s.underscore
path = send("edit_#{model_name}_path", object_instance)
end
#This will return an show link for the specified object instance
def show_path_for_object_instance(object_instance)
model_name = object_instance.class.to_s.underscore
path = send("#{model_name}_path", object_instance)
end
You can call the edit_path_for_object_instance method directly from your view and pass in the user.billing_profile object.
This will give you a link directly to the entity resulting a url like /billing_profile/ID/edit
An alternate approach is to use fields_for. This will allow you to create a form for the User attributes and update the associated BillingProfile at the same time. It would look something like this:
<%= form_for #user%>
<%= fields_for #user.billing_profile do |billing_profile_fields| %>
<%= billing_profile_fields.text_field :name %>
<% end %>
<%end%>
See here: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
Related
This question is regarding Rails 4/postgresql and the app is hosted on Heroku.
I am making a Quiz-functionality on a website and I am wondering on how to implement the forms (using Formtastic) best to make this is easy as possible. I have three models:
Quiz (has_many :quiz_questions), e.g. "Test to see how awesome you are"
QuizQuestion(belongs_to :quiz, has_many :quiz_options). e.g. "1. Which is your favorite color")
QuizOption (belongs_to :quiz_question). e.g. "Blue"
I have set up the forms like this:
<%= semantic_form_for([:admin, #quiz], :url => admin_quiz_path(#quiz.id)) do |f| %>
<%= render 'form' , :f => f %>
<% end %>
where the form looks like this:
<%= f.inputs %>
<h3>Quiz questions</h3>
<%= f.semantic_fields_for :quiz_questions do |qq_f| %>
<%= qq_f.inputs %>
<h4>Quiz options</h4>
<%= qq_f.semantic_fields_for :quiz_options do |qqo_f| %>
<%= qqo_f.inputs %>
<% end %>
<% end %>
<%= f.actions do %>
<%= f.action :submit %>
or go <%= link_to 'back', admin_quizzes_path %>
<% end %>
It seems, however, not to be working the way I want. I expect to be able to see the fields of QuizQuestion and QuizOptions in this form (there are objects for those) but I don't.
More importantly is that I would like to be able to create a New QuizQuestion and subsequently QuizOption in this form. It doesn't necessarily have to be jQuery/ajax or anything but I would like to do it all from this form.
Basically, I would like my workflow to be like:
Create a Quiz and add values to it. Click Create.
Add QuizQuestion number one and add the values to it (like "name label"). Click Create.
Add QuizOption related to QuizQuestion number one, and its "name label". Click create.
Repeat for QuizQuestion/QuizOption until the Quiz is done.
How can I do this?
For your workflow you might have to add accept_nested_attributes_for for the nested resources, this way when creating an object object you can actually create nested children (as long as they fulfill all the validations). This way:
# A quiz :has_many :quiz_questions
#quiz = Quiz.create(...)
with a declaration like:
has_many :quiz_questions
accepts_nested_attributes_for :quiz_questions
in your Quiz model you'll actually be able to create QuizQuestion from the quiz model like:
# using the previously quiz model
quiz.quiz_questions.create(...)
Doing the same for the deeply nested associations will do have the same effect.
Perhaps the reason why you don't see any field on the form is because there is not nested object created. Let me explain. When you create a new Quiz object, in your quizs_controller (or whatever the inflection for quiz is...) you need a:
def new
quiz = Quiz.new()
end
and
def create
Quiz.new(quiz_params)
end
private
def quiz_params
# whitelisted parameters sent along with the form
params.require(:quiz).permit(...)
end
if you actually want to be able to see the fields in the form you'll have to use the build method and actually populate that new object with respective the nested resources.
Note that for this to work with the form you will have to whitelist in the quizzes_controller the right attributes. You can debug the params you receive once you send the new quiz formulary and check that everything is right.
TIP! if you don't want to worry about the JS when adding nested resources dynamically, I recommend you using the cocoon gem
I have added a custom field in my spree_orders table (let's call it custom_attribute).
I have added Spree::PermittedAttributes.checkout_attributes << [:custom_attribute] to my spree.rb initializer.
In my checkout process I have a custom form with the following code (html formatting has been removed):
<%= form_for #order do |alt_form| %>
<%= alt_form.label :custom_attribute, "Custom Attribute" %><span class="required">*</span><br />
<%= alt_form.text_field :custom_attribute, :class => 'form-control required', maxlength: 11 %>
<% end %>
This form successfully submits the field in the post request (full dump below) to http://localhost:3000/checkout/update/address as order[custom_attribute] xyz, however, the information is not saved to the model.
_method=patch
_method=patch
authenticity_token=Y+ATRotWKfI57f+b0/YGwIw9Bg6mADHBDmeEOHYzLPnB6Vbydya4ITDTopcX65EG+TiL7bwyJKQPpBU9bQTaUg==
authenticity_token=Y+ATRotWKfI57f+b0/YGwIw9Bg6mADHBDmeEOHYzLPnB6Vbydya4ITDTopcX65EG+TiL7bwyJKQPpBU9bQTaUg==
commit=Save and Continue
order[bill_address_attributes][address1]=123 Test
order[bill_address_attributes][address2]=
order[bill_address_attributes][city]=Test
order[bill_address_attributes][country_id]=232
order[bill_address_attributes][firstname]=Test
order[bill_address_attributes][id]=3
order[bill_address_attributes][lastname]=Test
order[bill_address_attributes][phone]=555555555
order[bill_address_attributes][state_id]=3535
order[bill_address_attributes][zipcode]=30024
order[email]=spree#example.com
order[custom_attribute]=2414
order[state_lock_version]=32
utf8=✓
utf8=✓
I've inserted #order.inspect on the following (payment) page to can see at that point that #order.custom_attribute is still nil.
Does anyone have any idea about what I need to do in order to get the custom_attribute value sent in the post request saved to the model with the other attributes sent?
-------------------edit-------------------
Default spree permitted attributes are defined here https://github.com/spree/spree/blob/3-0-stable/core/lib/spree/core/controller_helpers/strong_parameters.rb and are added on by the strong_paramaters helper here (don't have the rep to post a third link):
module Spree
module Core
module ControllerHelpers
module StrongParameters
def permitted_attributes
Spree::PermittedAttributes
end
delegate *Spree::PermittedAttributes::ATTRIBUTES,
to: :permitted_attributes,
prefix: :permitted
def permitted_payment_attributes
permitted_attributes.payment_attributes + [
source_attributes: permitted_source_attributes
]
end
def permitted_checkout_attributes
permitted_attributes.checkout_attributes + [
bill_address_attributes: permitted_address_attributes,
ship_address_attributes: permitted_address_attributes,
payments_attributes: permitted_payment_attributes,
shipments_attributes: permitted_shipment_attributes
]
end
def permitted_order_attributes
permitted_checkout_attributes + [
line_items_attributes: permitted_line_item_attributes
]
end
def permitted_product_attributes
permitted_attributes.product_attributes + [
product_properties_attributes: permitted_product_properties_attributes
]
end
end
end
end
end
which can be at found spree/core/lib/spree/core/controller_helpers/strong_parameters.rb in the spree github repo.
-------------------final edit-------------------
If anyone finds this in the future and is trying to troubleshoot a similar issue, my code above is actually correct; I had (stupidly) placed it in an if Rails.env.production? block.
I will give you an example, maybe you can translate it into your code.
OPTIONAL
Imagine that I have a custom action, called "custom" on my users controller, defined this way in my routes:
resources :users do
collection do
get 'custom'
post 'custom'
end
end
This way I can call it by using custom_users_path.
Next, I want a form that submits to that function, to do that you need to specify an additional parameter in your form_for called :url, in this example I call it using custom_users_path, once I submit the form, It will run my custom action.
form_for would look like this:
<%= form_for :user, :url => custom_users_path do |f| %>
<%= f.text_field :random %>
<%= f.submit "Submit" %>
<% end %>
Then, I want to be able to access some :random parameter in my users controller. Let's suppose that I have a text_field which I want store the value on my :random parameter (see above). First, you need to permit that parameter to be accessible in your controller, in this example, in users controller. This way:
params.require(:user).permit(YOUR PARAMETER HERE, {:random => []})
So, every time I submit the form, I can access the :submit parameter value, by doing this params["controller-name"]["parameter-name"], translated into this example, would look like:
params["user"]["random"]
You can then convert it into string using to_s if you want.
Output (Supposing that I wrote "444" on my text_field):
444
I hope this helps you.
I stored all the tablename I've created to Menu table. And every time I add the table in Menu, it will automatically create a link under Menu list
see below.
I want each table in Menu to have a Listing, New, Edit, and Delete.
see below.
I have a controller prj_menus_controller, I will just pass the id of the table from Menu table.
here is the code for index and new in my controller.
Class PrjMenusController < ApplicationController
def index
#prj_menus = Menu.find(params[:id]).tablename.singularize.classify.constantize.all
end
def new
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new
end
def create
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new(prj_menu_params)
if #prj_menu.save
redirect_to :action => 'index'
else
render :new
end
end
private
def prj_menu_params
params.require("HERE IS MY PROBLEM").permit(:name)
end
end
and in my
new.html.erb
<%= simple_form_for (#prj_menu),:url => prj_menus_path, :method => :post do |f| %>
<%= f.input :name %>
<%= f.submit 'Save', class: 'btn btn-primary' %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
I can get the list in my index.html.erb, it is working. My problem is that I don't know how to get all params when I click the submit in new.html.erb. I got this hash
{"sample1_table"=>{"name"=>"test 6"}, "commit"=>"Save","controller"=>"prj_menus", "action"=>"create"}
It is correct but I don't know what to put in my controller. I tried this params.require(["#{#prj_menu}"]).permit(:name), it creates new record but params[:name] does not save.
I am still a noob to Ruby On Rails and I don't know what to search for this.
I think you are mostly confused on what parameter whitelisting does and how parameters are passed from the form to the controller.
I does not really matter if the name of the form hash matches the name of the database table. It just does in most cases since that makes the most sense. It's simply representative of the REST interface of your app.
Let's say you have a action which creates Pets:
POST /pets
And in our form we have a bunch of inputs like so:
<input name="pet[name]">
Rails will map create a params[:pet] hash { name: 'Spot' }. But we want to save the pets as an Animal.
class PetsController < ApplicationController
def new
#pet = Animal.new()
end
def create
#pet = Animal.new(pet_params)
if #pet.save
# ...
end
def pet_params
params.require(:pet).permit(:name)
end
end
Animal does not care what the params key is, it just gets a hash. But we also need to tell simple_form what parameter key we want to use since it looks at the model_name attribute.
simple_form_for(#pet, as: :pet)
Gives us pet[name] instead of animal[name].
I don't get why you are so adamant about making things so difficult for yourself though unless you are creating a database administration tool in the vein of PHP_MyAdmin. And even that case you don't even want to be altering the schema of the app database at runtime.
You are going to run into huge problems when it comes to creating effective queries for getting all the menus.
I have simple app where users can register when they pass Human validation.
For that my setup is like this:
Model:
include Humanizer
attr_accessor :bypass_humanizer
require_human_on :create, :unless => :bypass_humanizer
View:
<%= f.label :humanizer_answer, #advertisement.humanizer_question %>
<%= f.hidden_field :humanizer_question_id %>
This far everything works.
Also I want to allow admin user to register new users in ActiveAdmin panel.
As we now ActiveAdmin uses controller actions if we don't override them.
Based on Humanizer documentation I have to set bypass_humanizer to true when I want to disable Human validation.
So I am overriding create action like this:
controller do
def create
bypass_humanizer = true
super
end
end
But it don't want to work as expected.
Any help on this ?
Thanks in advance for your time.
ActiveAdmin.register Model do
before_create do |model|
model.bypass_humanizer = true
end
end
Or you can place a hidden input with name bypass_humanizer and value true in the form.
I'm building a martial arts related database, currently I have the following associations set up:
Student has_and_belongs_to_many :styles
Style has_many :ranks
Student has_many :ranks, through: :gradings (and vice versa)
I'm generating a form as follows, depending on the student's styles:
So the headings are generated by the Style model (Tai Chi, Karate...), then their rankings listed below (taken from the Rank model), and the "Dojo" and "Date" fields should belong to the Grading model once created.
The question: I know how to build a form that creates one association (or one association + its children), but how do I build a form that creates multiple associations at once?
Also, what would be a clean way to implement the following:
Only lines which are ticked become associations
Dojo and date must be filled in for ticked lines to save successfully
If a line is unticked it will destroy any previously created associations
This is what I've currently implemented to retrieve the correct records:
class GradingsController < ApplicationController
before_filter :authenticate_sensei!
def index
#student = Student.includes(:styles).find(params[:student_id])
#ranks = Rank.for_student_styles(#student)
split_ranks_by_style
end
private
def split_ranks_by_style
#karate = #ranks.select_style("Karate")
#tai_chi = #ranks.select_style("Tai Chi")
#weaponry = #ranks.select_style("Weaponry")
end
end
# Rank model
def self.for_student_styles(student)
includes(:style).where("styles.id in (?)", student.styles.map(&:id))
end
def self.select_style(style)
all.map { |r| r if r.style.name == style }.compact
end
Complicated forms like this are best handled in a service object initiated in the primary resource's create or update action. This allows you to easily find where the logic is happening afterwards. In this case it looks like you can kick off your service object in your GradingsController. I also prefer formatting a lot of the data in the markup, to make the handling easier in the service object. This can be done a'la rails, by passing a name like "grade[style]" and "grade[rank]". This will format your params coming in as a convenient hash: {grade: {style: "karate", rank: "3"}}. That hash can be passed to your service object to be parsed through.
Without really grasping the full extent of your specific requirements, let's put together an example form:
<%= form_for :grading, url: gradings_path do |f| %>
<h1><%= #rank.name %></h1>
<%- #grades.each do |grade| %>
<div>
<%= hidden_field_tag "grade[#{grade.id}][id]", grade.id %>
<%= check_box_tag "grade[#{grade.id}][active]" %>
...
<%= text_field_tag "grade[#{grade.id}][date]" %>
</div>
<%- end %>
<%= submit_tag %>
<%- end %>
With a form like this, you get your params coming into the controller looking something like this:
"grade"=>{
"1"=>{"id"=>"1", "active"=>"1", "date"=>"2013-06-21"},
"3"=>{"id"=>"3", "date"=>"2013-07-01"}
}
Nicely formatted for us to hand off to our service object. Keeping our controller nice and clean:
class GradingsController < ApplicationController
def index
# ...
end
def create
builder = GradeBuilder.new(current_user, params['grade'])
if builder.run
redirect_to gradings_path
else
flash[:error] = 'Something went wrong!' # maybe even builder.error_message
render :action => :index
end
end
end
So now we just need to put any custom logic into our builder, I'd probably recommend just making a simple ruby class in your /lib directory. It could look something like this:
class GradeBuilder
attr_reader :data, :user
def self.initialize(user, params={})
#user = user
#data = params.values.select{|param| param['active'].present? }
end
def run
grades = data.each{|entry| build_grade(entry)}
return false if grades.empty?
end
private
def build_grade(entry)
grade = Grade.find(entry['id'])
rank = grade.rankings.create(student_id: user, date: entry['date'])
end
end
There will obviously need a lot more work to pass all the specific data you need from the form, and extra logic in the GradeBuilder to handle edge cases, but this will give you a framework to handle this problem in a maintainable and extensible way.