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.
Related
I am working on status feature for my rails app. I have everything set up and it works perfectly fine. For the interface, I am using the best_in_place gem to allow users to edit in place for the status. The code is given below.
<% if #scoreboard.status.content.present? %>
<div id="statuscontent">
<%= render 'statusedit' %>
</div>
<% else %>
<%= render 'statusupdate' %>
<% end %>
The statusedit partial contains the best_in_place code and the statusupdate partial contains the normal form for creating the status. Codes for both partials are given below.
statusedit
<%= best_in_place [#scoreboard, #status], :content, place_holder: "Enter a status", as: "textarea" %>
<%= link_to "delete", [#scoreboard, #status], method: :delete, data: {confirm: "Are you sure"} %>
statusupdate
<%= form_for [#scoreboard, #status] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, class: "form-control", id:"status" %>
<%= f.submit "Post", class: "btn btn-primary", id: "status-button" %>
<% end %>
The above code is working perfectly fine, however, I am struggling to set up the interface I want. The problem is that best_in_place editing only works if the status has been created before. Therefore, I have to create the status using the form and then I can use in place editing. However, I want the interface such that users can use in place editing without the need of physically creating a status first. Is there a way to create a default status for every user? Or a before_update function that maybe creates a status before the user tries to update.
I tried using Ajax for creating status to achieve the interface but it didn't work as intended. Also, I feel there must be a simpler solution to this problem. I tried setting :default => "please upload status" in the database but that doesn't actually create a default record. It acts like a placeholder but you still have to click post for the record to be created. Is there a way to somehow automatically create a default status for each user or set a value in the database. I have read a few stack-overflow posts on this but nothing really points to the right direction. There must be a simpler way of doing this in rails. Any documentation or suggestions would be a great help. As always, any help is always greatly appreciated!! Thanks
I have added the relevant migration and model files.
status model
class Status < ActiveRecord::Base
belongs_to :scoreboard
end
scoreboard model
class Scoreboard < ActiveRecord::Base
has_one :status
end
Status migration file
class CreateStatuses < ActiveRecord::Migration
def change
create_table :statuses do |t|
t.text :content
t.references :scoreboard, index: true
t.timestamps null: false
end
add_foreign_key :statuses, :scoreboards
end
end
Answer for your questions.
Is there a way to create a default status for every user ?
Yes
You could create a default record in database after change_column.
def up
change_column :users, :admin, :boolean, default: false # I'm assuming you are saving default as false in users table but you could change accordingly all thing.
end
before_update function that maybe creates a status before the user tries to update.
instead of before_update one could use after_save callback.
As mentioned in official Document
after_save runs both on create and update, but always after the more
specific callbacks after_create and after_update, no matter the order
in which the macro calls were executed.
And lastly
There must be a simpler way of doing this in rails
Use ActiveRecord Migrations.
Apologies if this answer is not specific; you wrote a lot.
You're best setting a default value for an attribute at the database level.
Yes, you can use the before_create callback:
#app/models/scoreboard.rb
class Scoreboard < ActiveRecord::Base
before_create :set_default_status
private
def set_default_status
self[:status] = "default"
end
end
The problem with this is that it adds application-level logic which can be handled by the db:
$ rails g migration CreateStatusDefault
# db/migrate/create_status_default________.rb
class CreateStatusDefault < ActiveRecord::Migration
def change
change_column :scoreboards, :status, default: "Default"
end
end
$ rake db:migrate
Creating a default value in the db basically means that unless you send a value from your model at create / update, you're going to get the default.
The beauty of setting it in the DB is that your Rails app has absolutely zero work to do for it, making it much more efficient.
--
I subsequently found that your status "object" is actually a model in itself, which is probably where your issues are coming from.
If that is the case, you'll need to build the associative object before you create your main/parent one:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :statuses
before_create -> (model) { model.statuses.build }
end
We use this to build a profile for each User who signs up to our applications; it creates a "blank" associative record for your parent model.
This is something which would give you a "blank" status object, but as you've not posted your code, I really don't know.
A good tip with SO is code > text.
If in doubt, post code.
Is there a way to create a default status for every user?
Yep you can write it yourself. Something like this ...
User < ActiveRecord::Base
has_many :statuses
after_create :create_first_status
def create_first_status
self.statuses.create some_status_attribute: "some value"
end
end
There are many students and each student belongs to a program.
I have a controller:
def edit
#student = Student.find(params[:id])
#programs = Program.all
end
Then I have an edit form (simplified):
<%= f.label(:program_id, "Program") %>
<%= f.select(:program_id, #programs.collect {|c| [c.name, c.id]}) %>
Finally, I have a model for the student:
class Student < ActiveRecord::Base
belongs_to :program
validates :first_name, presence: true
end
I can update a student, as long as I input a first name. If I leave first name blank, I get an error undefined method 'collect' for nil:NilClass
However, if I change the form just a little bit, everything works. Like so:
<%= f.label(:program_id, "Program") %>
<%= f.select(:program_id, Program.all.collect {|c| [c.name, c.id]}) %>
Notice the Programs.all in there. Instead of in the controller.
What gives? I'd really like to define #programs in the controller. Am I missing something obvious?
Thanks!
P.S. If I take the validation rule out of the model, everything works again--so I'm pretty sure the validation is at the heart of the matter.
Do you have an update method in your controller? My guess is in the update action you have an if statement that says if the save fails, render the edit template again. If you are rendering the edit template from the update method, the view no longer knows about #programs, so you would need to redefine again in the update method, or create a before_action.
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
I've added an extra method to my model in rails, let's just say it is:
def subscribed
true
end
If I create a form with:
<%= f.check_box :subscribed %>
it correctly prints a ticked checkbox, but on submission of the form it says
unknown attribute: subscribed
What am I doing wrong?
Add an attribute accessor like this:
class MyModel
attr_accessor :subscribed
end
I've written a helper for my user model in user_helper.rb
module UserHelper
def get_array_of_names_and_user_ids
User.all(&:first_name) + User.all.map(&:user_id)
end
end
Unfortunately when I type in
<div class="field">
<%= f.label :assignee, "Assigned to" %>
<%= select(:task, :assignee_id, User.get_array_of_names_and_user_ids )%>
</div>
It can't see it. Where am I going wrong? I'm using devise.
You're close. The helper doesn't become a class method like that -- it becomes accessible as a method in your views. Just simply call get_array_of_names_and_user_ids.
Helpers are for views not for model.
For model you should define class methods in User model
class User
def self.get_array_of_names_and_user_ids
User.all(&:first_name) + User.all.map(&:user_id)
end
end
You don't need to hand code this helper as Rails provides a helper called collection_select for this purpose.
In your view simply add this:
<%= collection_select(:task, :assignee_id, User.all, :id, :first_name,
:prompt => true) %>
Note:
I am assuming you have a small set of users in your DB(<30). Otherwise, you have to use some other control to select users.
Helpers are methods that can be called in the view, not methods to be called on the model
Just call get_array_of_names_and_user_ids