update record outside of namespaced admin [rails] - ruby-on-rails

I have a named spaced admin in my app, where you can CRUD a client, this works great.
Outside of the admin within the app the #current_user has the opportunity to edit the client's address.
I am not sure how to access this and persist the new update to the database. this is what I have so far
products_controller.rb (where the partial is rendering from)
class ProductsController < ActionController::Base
layout "application"
def index
#products = Product.all
end
def show
#products = Product.all
#current_user = Client.find_by(id: session[:client])
end
end
_overlay_checkout.html.erb (this populates a form with the clients address from the db, and if its not updated they can change it.)
<%= simple_form_for([:admin, #current_user], :url => edit_admin_client_path) do |f| %>
<%= f.text_field :address %><br />
<%= f.text_field :address_line_2 %><br />
<%= f.select(:state, options_for_select(us_states, "CA")) %> <br />
<%= f.text_field :zipcode %><br />
<%= f.text_field :city %><br />
<%= f.submit %>
<% end %>
im unsure how I can update this record, ... without being in the admin section of the app, .. any help would be greatly appreciated.

One thing I've done in situations like this is to specify #submit_url as an instance variable in my controller action.
class ProductsController < ApplicationController
def show
#submit_url = client_path(#client)
end
end
You can can pass this variable to your form_for call like so:
<%= simple_form_for([:admin, #current_user], :url => #submit_path) do |f| %>
<%= f.text_field :address %><br />
<%= f.text_field :address_line_2 %><br />
<%= f.select(:state, options_for_select(us_states, "CA")) %> <br />
<%= f.text_field :zipcode %><br />
<%= f.text_field :city %><br />
<%= f.submit %>
<% end %>
If the #submit_url is not nil then the form action will be set to the value of the #submit_url otherwise it will get set to the resource based route that you specified. This means that in Admin Controller will not need to be changed at all. Just the alternate controller (in this case products_controller) where you want the submission of the form to go someplace other than the auto generated resourceful route.

Related

Create Devise User From A Different Form and Controller - Ruby on Rails

We have two models, a 'Devise User' and an 'Influencer'. An Influencer is a User, as such it must have a User (from the db standpoint). A User can be multiple other things. Thus, we want to have the ability to sign up a User without being an Influencer and we want to sign up a User when they want to sign up as an Influencer.
I have a form like so:
influencers/new.html.erb
<%= form_for #influencer do |i| %>
<%= i.fields_for(resource, as: resource_name, url: registration_path(resource_name)) do |u| %>
<div id="registration_fields">
<%= render 'devise/registrations/registration_fields', f: u %>
</div>
<% end %>
<div class='field'>
<%= i.label :twitter_handle %><br/>
<%= i.text_field :twitter_handle %>
</div>
<div class='field'>
<%= i.label :short_bio %><br/>
<%= i.text_area :short_bio %>
</div>
/views/devise/registrations/_registration_fields
<%= devise_error_messages! %>
<div class="field">
<%= f.label :first_name %> <br />
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name %> <br />
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
We've modified the ApplicationHelper and 'new' method so that it could render this Devise form without problems. Unfortunately, we are stuck as to how to properly make the 'create' method for our InfluencersController.
This is the hash we receive:
Parameters: {..., "influencer"=>{"user"=>{"first_name"=>"buddy", "last_name"=>"king", "email"=>"bdking#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "twitter_handle"=>"#bdking", "short_bio"=>"None"}, "commit"=>"Join as influencer"
Essentially we want to Devise to handle the user information while we handle the influencer information. We have tried calling the Devise::RegistrationsController.new.create method from within InfluencersController#create. However, this poses its own difficulties (even with multiple hacks we reach different problems such as, missing '#response' or missing 'response.env' or missing 'devise.mappings').
With that said, We believe that inheriting will allow us to call 'super' in the create function. However, we do not want to have InfluencersController inherit from Devise::RegistrationsController since this controller is not by any means a true Devise controller.
Is there any way we could get around this?
I would use the tried and true pattern of users and roles.
Basically your User class is in charge of authentication (identity) and the user has many Roles which can be used for authorization (permissions).
class User < ActiveRecord::Base
has_many :roles
def has_role?(role)
roles.where(name: role)
end
end
class Role < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :name, scope: :user_id
end
So an "influencer" is really just a user with a Role(name: "influencer") attached to it. The real power and flexibility is that it makes it trivial to implement granting/revoking roles from a web GUI.
And you don't have to mess around with how Devise/Warden handle authentication to support multiple classes which can get really messy.
Best part is that the Rolify gem makes it really trivial to set up.
If you need to setup a specific endpoint (influences/registrations) to register "influencers" you can simply override the build_resource method in the Devise controller:
class InfluencerRegistrationsController < Devise::RegistrationsController
def resource_class
User
end
def build_resource
super
self.resource.roles.new(name: :influencer)
end
end

Routes in ruby with rails, html forms not working

I am new to ruby with rails,I generated a new Controller named users by using command:
rails generate controller users register index login
After that I opened register.html.erb and wrote following code:
<h1>Register New User</h1>
<p>
<%= form_for :user do |f| %>
<%= f.label:USERID %><%= f.text_field:userid %><br />
<%= f.label:PASSWORD %><%= f.text_field:password %><br />
<%= f.label:EMAIL %><%= f.text_field:email %><br />
<br />
<%= f.submit %>
<% end %>
</p>
Then in users_controller.rb I wrote following code in register:
class UsersController < ApplicationController
def index
end
def show
end
def login
end
def register
print "test"
end
def user_params
params.require(:user).permit(:userid,:password,:email)
end
end
And test is not being printed and get and post methods of the form are not working at all.
And params.require(:user).permit(:userid,:password,:email) is not working as well. I get error that :user is empty.
I get error that :user is empty.
form_for(path/to/your/controller/action) is a helper method to create HTML form elements with the url path to the POST or GET request. The helper knows if it should be a new record or an update record based on what you are asking to do in your controller action. So, basically, it's looking for the #user in the corresponding controller action when you write form_for #user in your view. As you don't have it currently, hence you ar getting this error.
See this for more information.
To solve your current problem, you have to define the #user in the controller's register action:
def register
#user = User.new(user_params)
end
Then, in your register.html.erb file's form, you can use that #user:
<h1>Register New User</h1>
<p>
<%= form_for #user do |f| %>
<%= f.label :USERID %>
<%= f.text_field :userid %><br />
<%= f.label :PASSWORD %>
<%= f.text_field :password %><br />
<%= f.label :EMAIL %>
<%= f.text_field :email %><br />
<br />
<%= f.submit %>
<% end %>
</p>

Displaying nested models

My nested model form is now working, but I am having trouble displaying the data in a view. How do I display nested model data with a one-to-many relationship? Any help will be greatly appreciated.
Here's my form and controller:
<%= form_for #account do |f| %>
<%= f.label :account_type %><br />
<%= f.text_field :account_type %><br />
<%= f.fields_for :organizations do |builder| %>
<%= builder.label :name %><br />
<%= builder.text_field :name %><br />
<%= builder.label :website %><br />
<%= builder.text_field :website %><br />
<%= builder.fields_for :locations do |lb| %>
<%= lb.label :phone %><br />
<%= lb.text_field :phone %><br />
<%= lb.label :toll_free_phone %><br />
<%= lb.text_field :toll_free_phone %><br />
<%= lb.label :fax %><br />
<%= lb.text_field :fax %><br />
<%= lb.fields_for :addresses do |ab| %>
<%= ab.label :address1 %><br />
<%= ab.text_field :address1 %><br />
<%= ab.label :address2 %><br />
<%= ab.text_field :address2 %><br />
<%= ab.label :city %><br />
<%= ab.text_field :city %><br />
<%= ab.label :state %><br />
<%= ab.text_field :state %><br />
<%= ab.label :zip %><br />
<%= ab.text_field :zip %><br />
<% end %>
<% end %>
<% end %>
<%= f.submit "Add account" %>
<% end %>
class AccountsController < ApplicationController
def show
#account = Account.find(params[:id])
#organization = #account.organizations
end
def new
#account = Account.new
organization = #account.organizations.build
location = organization.locations.build
location.addresses.build
#header = "Create account"
end
def create
#account = Account.new(params[:account])
if #account.save
flash[:success] = "Account added successfully"
render 'show'
else
render 'new'
end
end
end
In general, how do I reference nested model data in a view when there is one-to-many relationship? Do I need to specify the child with some type of "where clause" like method?
Here is a simple example show.html.erb where I am trying to display the Name of the Organization that I just created. It doesn't work.
<h1><%= #organization.name %></h1>
The render 'show' action after creating an Account with the above form results in this error:
NoMethodError in Accounts#create
Showing C:/Documents and Settings/Corey Quillen/My
Documents/rails_projects/shop_manager/app/views/accounts/show.html.erb where line #1
raised:
undefined method `name' for nil:NilClass
Extracted source (around line #1):
1: <h1><%= #organization.name %></h1>
Rails.root: C:/Documents and Settings/Corey Quillen/My
Documents/rails_projects/shop_manager
Application Trace | Framework Trace | Full Trace
app/views/accounts/show.html.erb:1:in
`_app_views_accounts_show_html_erb__790921876_14235864__946051513'
app/controllers/accounts_controller.rb:21:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"y59rGAhS+kqfH3v3axhlYuxvBbBxIWXg0yucCFwfBq8=",
"account"=>{"account_type"=>"dfdf",
"organizations_attributes"=>{"0"=>{"name"=>"dfdf",
"website"=>"dfdf",
"locations_attributes"=>{"0"=>{"phone"=>"dfdf",
"toll_free_phone"=>"dfd",
"fax"=>"",
"addresses_attributes"=>{"0"=>{"address1"=>"",
"address2"=>"",
"city"=>"",
"state"=>"",
"zip"=>""}}}}}}},
"commit"=>"Add account"}
You say #organization = #account.organizations in your show action.
Just what are you expecting #organization to contain?
Think about it. It's going to be an array not a single organization so loop through that and get the name for each organization
Actually I think you haven't thought your relationships through properly surely an account belongs_to an organisation. Do you really want an account to be associated with more than one organisation ?
UPDATE - Ref comment below
That is totally possible to do but you need to decide what the business logic is that determines which organisation needs to be displayed here.
If you can explain exactly how your relationships are supposed to work it shouldn't be too difficult to show you how to apply your logic
UPDATE - How to get the primary organisation
This is simply a matter of setting up a new association on the Account model
has_one :primary_organization.
:class_name => 'Organization',
:conditions => ['primary = ?', true]
Then in your show action just write
#account.primary_organization.first #because anything use on a find other than the primary key will always return an array even if there is only one record.
You might also want to check that primary_organization is not empty?
Consider refactoring that has_one into a class method. May not be necessary depending on your needs
Read more here http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
As your relation is one-to-many, you have to use
#organization.first.name
it will show first organization.
as #organization is a array you have to collect all names and then show.

Redirect to SHOW action in another controller

After creating a Person associated with a specific Account, how do I redirect back to the Account page?
The account_id is passed to the CREATE Person action via a URL parameter as follows:
http://localhost:3000/people/new?account_id=1
Here is the code:
<h2>Account:
<%= Account.find_by_id(params[:account_id]).organizations.
primary.first.name %>
</h2>
<%= form_for #person do |f| %>
<%= f.hidden_field :account_id, :value => params[:account_id] %><br />
<%= f.label :first_name %><br />
<%= f.text_field :first_name %><br />
<%= f.label :last_name %><br />
<%= f.text_field :last_name %><br />
<%= f.label :email1 %><br />
<%= f.text_field :email1 %><br />
<%= f.label :home_phone %><br />
<%= f.text_field :home_phone %><br />
<%= f.submit "Add person" %>
<% end %>
class PeopleController < ApplicationController
def new
#person = Person.new
end
def create
#person = Person.new(params[:person])
if #person.save
flash[:success] = "Person added successfully"
redirect_to account_path(params[:account_id])
else
render 'new'
end
end
end
When I submit the above form I get the following error message:
Routing Error
No route matches {:action=>"destroy", :controller=>"accounts"}
Why is the redirect_to routing to the DESTROY action? I want to redirect via the SHOW action. Any help will be greatly appreciated.
params[:account_id] exists in the form, but when you pass it to create you're sending it along in the person hash, so you'd access it via params[:person][:account_id]
params[:account_id] is nil, hence the bad route. To be honest, I'm not sure why, but resource_path(nil) ends up routing to destroy instead of show. In either case, it's a broken route without an id parameter.
# so you *could* change it to:
redirect_to account_path(params[:person][:account_id])
# or simpler:
redirect_to account_path(#person.account_id)
# but what you probably *should* change it to is:
redirect_to #person.account
Rails will inherently understand this last option, ascertaining the path from the class of the record, and getting the id from #to_param
I would not be pass this through using a hidden_field. Instead, use nested resources:
resources :account do
resources :people
end
Then have an account object for the form:
<%= form_for [#account, #person] do |f| %>
...
<% end %>
This #account object should be set up in the action that renders the form with a line like this:
#acccount = Account.find(params[:account_id])
Then when the form is submitted you'll have params[:account_id] in that action without the ugly hidden_field hack to get it there.
Yippee!

Rails populate edit form for non-column attributes

I have the following form:
<% form_for(#account, :url => admin_accounts_path) do |f| %>
<%= f.error_messages %>
<%= render :partial => 'form', :locals => {:f => f} %>
<h2>Account Details</h2>
<% f.fields_for :customer do |customer_fields| %>
<p>
<%= customer_fields.label :company %><br />
<%= customer_fields.text_field :company %>
</p>
<p>
<%= customer_fields.label :first_name %><br />
<%= customer_fields.text_field :first_name %>
</p>
<p>
<%= customer_fields.label :last_name %><br />
<%= customer_fields.text_field :last_name %>
</p>
<p>
<%= customer_fields.label :phone %><br />
<%= customer_fields.text_field :phone %>
</p>
<% end %>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
As well as
attr_accessor :customer
And I have a before_create method for the account model which does not store the customer_fields, but instead uses them to submit data to an API.. The only thing I store are in the form partial..
The problem I'm running into is that when a validation error gets thrown, the page renders the new action (expected) but none of the non-column attributes within the Account Detail form will show?
Any ideas as to how I can change this code around a bit to make this work me??
This same solution may be the help I need for the edit form, I have a getter for the data which it asks the API for, but without place a :value => "asdf" within each text box, it doesn't populate the fields either..
Okay, what you need to do is create a class to handle your customer with and without a Braintree gateway connection. First, create the class:
class Customer
attr_accessor :company, :first_name, :last_name, :phone, :gateway
def initialize gateway_id=nil
begin
#gateway = Braintree::Customer.find(gateway_id) unless gateway_id.nil?
rescue Braintree::NotFoundError
end
end
def company
#gateway.nil? ? #company : #gateway.company
end
# Implement the rest of the methods this way as well. You can even use
# meta-programming so that you don't repeat yourself.
end
You'll notice that calling Customer.new(id).company will work with and without an id or gateway, because if a gateway non-existent #company will be returned, and if a gateway is present the gateway's company will be returned.
Finally, hook this into your model:
class Account
def customer
#customer ||= Customer.new(self.gateway_customer_id)
end
def customer= h
#customer = Customer.new
#customer.company = h[:company]
...
#customer
end
end
You'll have to modify how you write code to the API so that you use customer.company instead of customer[:company] for example, but you can probably write a function within the Customer class to do this easily.
You'll have to modify your form to:
<% f.fields_for :customer, #account.customer do |customer_fields| %>

Resources