Rails and devise - assign group when user signs up - ruby-on-rails

I have an app with multiple plans. For the "team" plan, the user will enter a team name and license count. When the user is created, a new team will be saved. I think I have the database part correct but I am confused with how to create the team. More specifically, my questions are: 1) How do I add the team fields to the view? Do I use form_for and nest that inside the user form? 2) How do I handle the team creation in the controller? Thanks for any help.
migration:
class CreateTeams < ActiveRecord::Migration
def change
create_table :teams do |t|
t.string :team_name
t.integer :license_count
t.timestamps
end
add_column :users, :team_id, :integer, :null => true
add_column :users, :team_admin, :boolean
end
end
controllers:
class RegistrationsController < Devise::RegistrationsController
def new
return if plan_missing
build_resource({})
self.resource.plan = params[:plan]
respond_with self.resource
end
protected
#after user has signed up
def after_inactive_sign_up_path_for(resource)
new_user_session_path
end
#after user has updated their profile, keep them on the same page
def after_update_path_for(resource)
edit_user_registration_path
end
private
def plan_missing
if params[:plan].nil?
redirect_to plans_path
true
else
false
end
end
def log
logger.debug #user.to_yaml
logger.debug params.to_yaml
end
end
models:
class User < ActiveRecord::Base
include KeyCreator
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
belongs_to :teams
has_many :document_users
has_many :documents, :through => :document_users
has_many :user_billing_events, dependent: :destroy
before_create :before_create
before_destroy :before_destroy
after_destroy :after_destroy
#scopes for activeadmin
scope :new_users, -> { where("created_at >= ? and created_at <= ?", Date.today.beginning_of_day.utc, Date.today.end_of_day.utc) }
scope :admin, -> { where(:admin => true) }
validates_inclusion_of :plan, in: %w( basic pro ), message: "Plan is not valid"
attr_accessor :stripe_token, :coupon, :to_skip
def is_admin?
self.admin
end
def paid_plan
["pro", "team"].include? self.plan
end
def self.save_event(customer_id, event_type)
user = User.find_by(customer_id: customer_id)
UserBillingEvent.create!(user: user, event_type: event_type)
end
def update_card(user)
c = Stripe::Customer.retrieve(customer_id)
c.card = user.stripe_token
c.coupon = user.coupon unless user.coupon.blank?
c.save
self.customer_id = c.id
self.last_4_digits = c.cards.data.first["last4"]
self.stripe_token = nil
self.save!
true
rescue Stripe::StripeError => e
logger.error "Stripe Error: " + e.message
errors.add :base, "#{e.message}."
self.stripe_token = nil
false
end
def update_plan(user)
self.stripe_token = user.stripe_token
self.coupon = user.coupon
if self.paid_plan && !user.paid_plan
cancel_stripe
end
if !self.paid_plan && user.paid_plan
setup_stripe
end
self.plan = user.plan
self.save!
true
end
def before_create
self.api_key = create_key
self.admin = false
setup_stripe if self.paid_plan
end
def before_destroy
cancel_stripe if self.paid_plan
end
def after_destroy
self.documents.where(:document_users => {:role => "owner"}).destroy_all
self.document_users.destroy_all
end
private
def setup_stripe
return if to_skip && to_skip.include?("stripe")
logger.debug '-- setting up stripe... --'
raise "Stripe token not present. Can't create account." if !stripe_token.present?
if coupon.blank?
customer = Stripe::Customer.create(:email => self.email, :description => self.name, :card => self.stripe_token, :plan => self.plan)
else
customer = Stripe::Customer.create(:email => self.email, :description => self.name, :card => self.stripe_token, :plan => self.plan, :coupon => self.coupon)
end
self.last_4_digits = customer.cards.data.first["last4"]
self.customer_id = customer.id
self.stripe_token = nil
customer
rescue Stripe::StripeError => e
logger.error "Stripe Error: " + e.message
errors.add :base, "#{e.message}."
self.stripe_token = nil
nil #return nil
end
def cancel_stripe
return if to_skip && to_skip.include?("stripe")
logger.debug '-- cancelling stripe... --'
customer = Stripe::Customer.retrieve(customer_id)
return false if customer.nil?
subscription = customer.subscriptions.data[0]
customer.cancel_subscription if subscription.status == 'active'
self.last_4_digits = nil
self.customer_id = nil
self.stripe_token = nil
true
rescue Stripe::StripeError => e
logger.error "Stripe Error: " + e.message
errors.add :base, "#{e.message}."
self.stripe_token = nil
false
end
def trace
logger.debug '-- tracing self in user.rb'
logger.debug self.to_yaml
logger.debug '--------------------------'
end
end
class Team < ActiveRecord::Base
has_many :users
end
sign up view:
<% content_for :head do %>
<%= tag :meta, :name => "stripe-key", :content => ENV["STRIPE_PUBLIC_KEY"] %>
<%= javascript_include_tag "https://js.stripe.com/v2/" %>
<%= javascript_include_tag "stripe/application", "data-turbolinks-track" => true %>
<% end %>
<div class="container">
<h2>Sign up for <%= #user.plan.titleize %></h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name),
:html => {:role => "form", class: #user.paid_plan ? "card-form" : ""} ) do |f| %>
<%= devise_error_messages! %>
<%= f.hidden_field :plan, :value => #user.plan %>
<% if #user.plan == "team" %>
<h3 class="vpad-top5">Team Administrator - User Account</h3>
<% end %>
<div class="form-group">
<label for="name">Display Name</label>
<%= f.text_field :name, :autofocus => true, :class =>"form-control" %>
</div>
<div class="form-group">
<label for="email">Email</label>
<%= f.email_field :email, :class =>"form-control" %>
</div>
<div class="form-group">
<label for="password">Password</label>
<%= f.password_field :password, :class =>"form-control" %>
</div>
<div class="form-group">
<label for="password_confirmation">Password confirmation</label>
<%= f.password_field :password_confirmation, :class =>"form-control" %>
</div>
<% if #user.plan == "team" %>
<h3 class="vpad-top5">Team Information</h3>
<!-- I need to add fields here! -->
<% end %>
<% if #user.paid_plan %>
<h3 class="vpad-top5">Payment Information</h3>
<%= render :partial => "shared/payment_fields", :locals => {:f => f} %>
<% end %>
<%= f.submit "Sign up",:class=>'btn btn-primary' %>
<% end %>
<%= render "devise/shared/links" %>
</div>

Use Rolify gem to manage user roles.
https://github.com/RolifyCommunity/rolify
You can set a default role on user Sign Up.

Related

Rails 5: given these three model associations...am I setting up my nested form field correctly?

Ok...am I doing this correctly? Am I setting up the nested form right given these model associations?
(should I include the volunteer_events controller and the volunteer_shift controller code here as well?)
volunteer_event model
class VolunteerEvent < ApplicationRecord
belongs_to :volunteer_default_event
validates_presence_of :date
has_many :volunteer_shifts, :dependent => :destroy
has_many :resources_volunteer_events, :dependent => :destroy
validates_associated :volunteer_shifts
def date_anchor
self.date.strftime('%Y%m%d')
end
# def date
# self.date
# end
def to_s
description
end
def time_range_s
return '0-0' if self.volunteer_shifts.length == 0
my_start_time = self.volunteer_shifts.sort_by(&:start_time).first.start_time
my_end_time = self.volunteer_shifts.sort_by(&:end_time).last.end_time
(my_start_time.strftime("%I:%M") + ' - ' + my_end_time.strftime("%I:%M")).gsub( ':00', '' ).gsub( ' 0', ' ').gsub( ' - ', '-' ).gsub(/^0/, "")
end
def merge_similar_shifts
hash = {}
self.volunteer_shifts.each{|y|
k = (y.volunteer_task_type_id.to_s || "0") + "-" + (y.description || "")
hash[k] ||= []
hash[k] << y
}
hash.each{|k, v|
prev_length = 0
length = v.length
while length != prev_length
v.each{|first|
v.each{|second|
next if first.id == second.id
combine = false
if ((first.end_time == second.start_time) or (first.start_time == second.end_time)) and (first.slot_number == second.slot_number)
first.start_time = [first.start_time, second.start_time].min
first.end_time = [first.end_time, second.end_time].max
combine = true
end
if combine
first.assignments << second.assignments
second.destroy
v.delete(second)
first.save!
break
end
}
}
prev_length = length
length = v.length
end
}
end
def copy_to(date, time_shift, copy_for)
new = self.class.new(self.attributes)
assigns = []
new.volunteer_shifts = self.volunteer_shifts.map{|x|
n = x.class.new(x.attributes); n.time_shift(time_shift);
if copy_for.include?(n.volunteer_task_type_id)
x.assignments.select{|x| x.contact_id && (!x.cancelled?)}.each do |y|
a = y.class.new(y.attributes)
a.time_shift(time_shift)
a.call_status_type_id = nil
a.attendance_type_id = nil
a.volunteer_shift = n
assigns << a
end
end
n
}
new.resources_volunteer_events = self.resources_volunteer_events.map{|x| x.class.new(x.attributes)}
new.date = date
new.resources_volunteer_events.each{|x| x.time_shift(time_shift)}
conflictors = assigns.select{|x| x.internal_date_hack_value = date; (!x.contact.nil?) && (!x.contact.is_organization) && x.find_overlappers(:for_contact).length > 0}
if conflictors.length == 0
new.save!
new.volunteer_shifts.each{|x| x.save!}
assigns.each{|x| x.save!}
return new
else
new.destroy if new.id
return conflictors
end
end
end
volunteer_shift model
class VolunteerShift < ApplicationRecord
validates_presence_of :roster_id
validates_presence_of :end_time
validates_presence_of :start_time
belongs_to :volunteer_default_shift
belongs_to :volunteer_task_type
has_many :assignments
belongs_to :program
belongs_to :roster
belongs_to :volunteer_event
has_many :contact_volunteer_task_type_counts, :primary_key => 'volunteer_task_type_id', :foreign_key => 'volunteer_task_type_id' #:through => :volunteer_task_type
...
end
assignment model
class Assignment < ApplicationRecord
attr_accessor :volunteer_shift #,:contact_id ???
belongs_to :volunteer_shift
has_one :volunteer_task_type, :through => :volunteer_shift, :source => :volunteer_task_type
belongs_to :contact ,optional: true
validates_presence_of :volunteer_shift
validates_associated :volunteer_shift
belongs_to :attendance_type
belongs_to :call_status_type
validates_presence_of :set_date, :if => :volshift_stuck
# validates_existence_of :contact, :allow_nil => true <----THIS IS BAD NEIBER
accepts_nested_attributes_for :volunteer_shift, allow_destroy: true
#fixme: Nodule::DelegationError -
# This error appears when trying to show the "Add New Assignment" model on the /assignments view
delegate :set_date, :set_date=, :to => :volunteer_shift
delegate :set_description, :set_description=, :to => :volunteer_shift
scope :date_range, lambda { |range|
joins(volunteer_shift: :volunteer_event)
.where(volunteer_shifts: { volunteer_events: { date: range } })
}
scope :is_after_today, lambda {||
{ :conditions => ['(SELECT date FROM volunteer_events WHERE id = (SELECT volunteer_event_id FROM volunteer_shifts WHERE id = assignments.volunteer_shift_id)) > ?', Date.today] }
}
scope :on_or_after_today, lambda {||
{ :conditions => ['(SELECT date FROM volunteer_events WHERE id = (SELECT volunteer_event_id FROM volunteer_shifts WHERE id = assignments.volunteer_shift_id)) >= ?', Date.today] }
}
scope :not_cancelled, -> { where('(attendance_type_id IS NULL OR attendance_type_id NOT IN (SELECT id FROM attendance_types WHERE cancelled = \'t\'))')}
scope :roster_is_limited_by_program, -> {where("roster_id IN (SELECT id FROM rosters WHERE limit_shift_signup_by_program = 't')").joins(:volunteer_shift)}
attr_accessor :attendance_type_id
...
end
assignments_controller
class AssignmentsController < ApplicationController
before_action :set_assignment, only: [:show, :edit, :update, :destroy]
skip_before_action :verify_authenticity_token #TODO refactor this line to be very specific
# GET /assignments or /assignments.json
def index
# #assignments = Assignment.limit(20)
# #assignments = Assignment.where(start: params[:start]..params[:end])
#assignments = Assignment.date_range(params[:start]..params[:end])
end
# GET /assignments/1 or /assignments/1.json
def show
end
# GET /assignments/new
def new
#assignment = Assignment.new
#fixme: build goes here
#assignment.volunteer_shift.build
#my_url = {:action => "create", :id => params[:id]}
end
# GET /assignments/1/edit
def edit
end
# POST /assignments or /assignments.json
def create
#assignment = Assignment.new(assignment_params)
respond_to do |format|
if #assignment.save
format.html { redirect_to #assignment, notice: "Assignment was successfully created." }
format.json { render :show, status: :created, location: #assignment }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #assignment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /assignments/1 or /assignments/1.json
def update
#assignment.update(assignment_params)
end
# DELETE /assignments/1 or /assignments/1.json
def destroy
#assignment.destroy
# NOTE: comment original out 4 now
# respond_to do |format|
# format.html { redirect_to assignments_url, notice: "Assignment was successfully destroyed." }
# format.json { head :no_content }
# end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_assignment
#assignment = Assignment.find(params[:id])
end
# Only allow a list of trusted parameters through.
def assignment_params
#fixme: ,volunteer_shift_attributes: [:???, :???, :???] <--- insert this below?
params.require(:assignment).permit(:title,
:redirect_to, :set_date, :date_range, :contact_id,
:start_time, :end_time, :start, :end, :attendance_type_id, :notes,
:call_status_type_id, :closed, :lock_version, :color,
volunteer_shift_attributes: [:volunteer_task_type_id,:roster_id,:program_id,:set_description,:set_date,:id,:destroy])
end
end
views/assignment/_form.html.erb
<%= form_for #assignment, :url => #my_url, remote: true do |f| %>
...
<%= f.fields_for :volunteer_shift do |builder| %>
<%= render 'volunteer_shift_fields', vs: builder %> <=====NESTED FORM THINGIE HERE...
<% end %>
...
<% end %>
_volunteer_shift_fields.html.erb <--- nested form partial
<div class="name large flex-row">
<%= vs.label :volunteer_shift %>
</div>
<div id="volunteer_shift" class="d-flex flex-row">
<div class="col-sm-12 p-2">
<div id="volunteer_shift" class="text-right">
<!-- old if: if class is assignment show volunteer shift else show default shift -->
<!-- we need default shift here...NO assignment is attached-->
<div class="field">
<%= vs.label :volunteer_task_type_id %>
<%= select_tag 'volunteer_task_type_id', options_from_collection_for_select([VolunteerTaskType.new(:description => ""), VolunteerTaskType.instantiables.effective_on(Date.today)].flatten, "id", "description") %>
</div>
<div class="field">
<%= vs.label :roster_id %>
<%= select_tag 'roster_id', options_from_collection_for_select([Roster.new(:name => ""), Roster.all].flatten, "id", "name") %>
</div>
<div class="field">
<%= vs.label :program_id %>
<%= select_tag 'program_id', options_from_collection_for_select([Program.new(:name => ""), Program.where(:volunteer => true)].flatten, "id", "name")%>
</div>
<div class="field">
<%= vs.label :set_description %>
<%= vs.text_field(:set_description, nil) %>
</div>
<div class="field">
<%= vs.label :set_date, "Date" %> <
<%= vs.text_field(:set_date, nil) %>
</div>
</div>
</div>
</div>

undefined method `scard' for nil:NilClass

app/models/user.rb:51:in `cart_count'
app/views/layouts/_header.html.erb:24:in `block in _app_views_layouts__header_html_erb__3312865121531214569_70179387150260'
app/views/layouts/_header.html.erb:23:in `_app_views_layouts__header_html_erb__3312865121531214569_70179387150260'
app/views/users/show.html.erb:40:in `_app_views_users_show_html_erb___592785710009336321_70179415740600'
When I attempt to view page after identification, I get the error above.
Here's my layouts/header.html.erb code:
<% if signed_in? %>
<%= link_to accueilconnect_path do%>
<li class="forma">Accueil</li>
<% end %>
<%= link_to nosformations_path do%>
<li class="contac">Formations</li>
<% end %>
<%= link_to devenirformateur_path do%>
<li class="contac">Devenir formateur</li>
<% end %>
<%= link_to contacts_path do%>
<li class="contac">Contact</li>
<% end %>
<%= link_to cart_path do%>
<i class="fi-shopping-cart"></i> My Cart (<span class="cart-count"><%=current_user.cart_count%></span>)
<%end%>
<%= link_to image_tag('user2.png', :class => "user_icon"), current_user %>
<% end %>
and here is my User Model :
class User < ActiveRecord::Base
attr_accessor :password
before_save { self.email = email.downcase }
attr_accessible :name, :email, :login, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :login, :presence => true,
:length => { :maximum => 20 }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_create :confirmation_token
before_create :encrypt_password
# Retour true (vrai) si le mot de passe correspond.
def has_password?(password_soumis)
encrypted_password == encrypt(password_soumis)
# Compare encrypted_password avec la version cryptée de
# password_soumis.
end
def authenticate(submitted_password)
self.has_password?(submitted_password)
end
def self.authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
(user && user.salt == cookie_salt) ? user : nil
end
def email_activate
self.email_confirmed = true
self.confirm_token = nil
save!(:validate => false)
end
def cart_count
$redis.scard "cart#{id}"
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
def confirmation_token
if self.confirm_token.blank?
self.confirm_token = SecureRandom.urlsafe_base64.to_s
end
end
end
problem is in :
def cart_count
$redis.scard "cart#{id}"
end
any idea ?
Did you installed Redis Server:
http://redis.io/download
and Run it locallay go to where redis folder exist and write
redis-server
and define it in intalizers:
$redis = Redis.new

Can't mass-assign protected attributes: _destroy

I'm getting this really weird error when I submit my nested form.
Can't mass-assign protected attributes: _destroy
Any idea why this may be? It's a bit of a concern as I'm having to remove the 'destroy' hidden_field with javascript temporarily until i figure out what it is, meaning i can't delete anything!
_form.html.erb
<%= nested_form_for(#post, :html=> {:multipart => true, :class=> "new_blog_post", :id=> "new_blog_post"}) do |f| %>
<%= field do %>
<%= f.text_field :title, placeholder: "Give your post a title", :class=>"span12" %>
<% end %>
<%= field do %>
<%= f.text_area :body, placeholder: "Write something here...", :id=>"blog-text", :class=>"span12" %>
<% end %>
<%= f.label :search_locations, "Add locations to your post" %>
<%= text_field_tag :name,"",:class=>"localename", :id=>"appendedInput", :placeholder=> "Name of the location", :autocomplete => "off" %>
<%= f.link_to_add "Add a location", :locations %>
<%= actions do %>
<%= f.submit "Submit", :class=>"btn", :disable_with => 'Uploading Image...' %>
<% end end%>
_posts_controller.rb_
class PostsController < ::Blogit::ApplicationController
...
def new
#post = current_blogger.blog_posts.new(params[:post])
#location = #post.locations.build
end
def edit
#post = Post.find(params[:id])
##post = current_blogger.blog_posts.find(params[:id]) removed so any use can edit any post
#location = #post.locations.build
end
def create
location_set = params[:post].delete(:locations_attributes) unless params[:post][:locations_attributes].blank?
#post = current_blogger.blog_posts.new(params[:post])
#post.locations = Location.find_or_initialize_location_set(location_set) unless location_set.nil?
if #post.save
redirect_to #post, notice: 'Blog post was successfully created.'
else
render action: "new"
end
end
def update
#post = current_blogger.blog_posts.find(params[:id])
if #post.update_attributes(params[:post])
redirect_to #post, notice: 'Blog post was successfully updated.'
else
render action: "edit"
end
end
def destroy
#post = current_blogger.blog_posts.find(params[:id])
#post.destroy
redirect_to posts_url, notice: "Blog post was successfully destroyed."
end
location.rb
class Location < ActiveRecord::Base
after_save { |location| location.destroy if location.name.blank? }
has_many :location_post
has_many :posts, :through => :location_post
has_many :assets
attr_accessible :latitude, :longitude, :name, :post_id, :notes, :asset, :assets_attributes
accepts_nested_attributes_for :assets, :allow_destroy => true
include Rails.application.routes.url_helpers
def self.find_or_initialize_location_set(location_set)
locations = []
locations = locations.delete_if { |elem| elem.flatten.empty? }
location_set.each do |key, location|
locations << find_or_initialize_by_name(location)
end
locations
end
end
EDIT:
Snippet of rendered form in new.html.erb
<div class="row span locsearch">
<div class="input-append span3">
<input autocomplete="off" class="localename" id="appendedInput" name="name" placeholder="Name of the location" type="text" value="">
<span class="add-on"><input id="post_locations_attributes_0__destroy" name="post[locations_attributes][0][_destroy]" type="hidden" value="false"><i class="icon-trash"></i></span> </div>
<div class="latlong offset3 span4"> <p class="help-block">Enter the name of the town or city visited in this blog entry.</p>
</div>
<input class="LegNm" id="post_locations_attributes_0_name" name="post[locations_attributes][0][name]" type="hidden" value="Dresden">
<input class="long" id="post_locations_attributes_0_longitude" name="post[locations_attributes][0][longitude]" type="hidden" value="13.7372621">
<input class="lat" id="post_locations_attributes_0_latitude" name="post[locations_attributes][0][latitude]" type="hidden" value="51.0504088">
</div>
</div>
EDIT2:
post.rb
class Post < ActiveRecord::Base
require "acts-as-taggable-on"
require "kaminari"
acts_as_taggable
self.table_name = "blog_posts"
self.paginates_per Blogit.configuration.posts_per_page
# ==============
# = Attributes =
# ==============
attr_accessible :title, :body, :tag_list, :blogger_id, :coverphoto, :locations_attributes
# ===============
# = Photo Model =
# ===============
has_attached_file :coverphoto,
:styles => {
:coverbar => "600x300>", :medium => "250x250^" , :thumb => "100x100^"},
#:source_file_options => {:all => '-rotate "-90>"'},
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:bucket => "backpackbug",
:path => "/:style/:id/:filename"
# ===============
# = Validations =
# ===============
validates :title, presence: true, length: { minimum: 6, maximum: 66 }
validates :body, presence: true, length: { minimum: 10 }
validates :blogger_id, presence: true
# =================
# = Associations =
# =================
belongs_to :blogger, :polymorphic => true
has_many :location_post
has_many :locations, :through => :location_post
belongs_to :profile
accepts_nested_attributes_for :locations, :allow_destroy => true, :reject_if => proc { |attributes| attributes['name'].blank? }
end end
This was solved with a combination of this and another answer, found here:
How can I fix this create function?
A short term fix is to add attr_accessible :_destroy and attr_accessor :_destroy.
Thanks both!
Add :_destroy to your attr_accessible list
attr_accessible ...., :_destroy
you should write to post.rb,
attr_accessible: locations_atributes
and
accepts_nested_attributes_for :locations, :allow_destroy => true
as you are updating the location object via post object.
replace f.hidden_field it must stay thus(line 2)
module ApplicationHelper
def link_to_remove_fields(name, f)
text_field_tag(:_destroy) + link_to_function(name, "remove_fields(this)")
end

Can't mass-assign protected attributes: stripe_card_token

I'm trying to create a charge with stripe. I get the following error when attempting to create order object, but I have set attr_accessor :stripe_card_token. Does anyone know what I am doing wrong?
ActiveModel::MassAssignmentSecurity::Error in OrdersController#create
Can't mass-assign protected attributes: stripe_card_token
OrdersController - Create action
def create
#order = current_cart.build_order(params[:order])
#order.ip_address = request.remote_ip
#order.user_id = current_user.id
respond_to do |format|
if #order.save_with_payment
#order.add_line_items_from_cart(current_cart)
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { render :action => "success", :notice => 'Thank you for your order.' }
format.xml { render :xml => #order, :status => :created, :location => #order }
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors,
:status => :unprocessable_entity }
end
end
end
OrderModel
class Order < ActiveRecord::Base
# PAYMENT_TYPES = [ "visa", "master card", "Amex", "Discover" ] Controll the payment options via Model
attr_accessible :first_name, :last_name, :ip_address, :cart_id, :house_id
attr_accessor :stripe_card_token
belongs_to :user
belongs_to :house
belongs_to :cart
has_many :transactions, :class_name => "OrderTransaction"
has_many :line_items, :dependent => :destroy
validates :house_id, presence: true
validates :cart_id, presence: true
def price_in_cents
(cart.total_price*100).round
end
def add_line_items_from_cart(cart)
cart.line_items.each do |item|
item.cart_id = nil
line_items << item
end
end
def save_with_payment
if valid?
Stripe::Charge.create(amount: price_in_cents, currency: "cad", description: current_user.name, card: stripe_card_token)
# self.stripe_order_token = order.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
OrderView _Form
<%= f.error_notification %>
<%= f.error_messages %>
<%= f.hidden_field :stripe_card_token %>
<%= f.hidden_field :cart_id%>
<div class="form-inputs">
<p><%#if user does not have a house Make a page (please order a home valuation first) %></p>
<div class="contain">
<h3>Select House</h3>
<%= f.input :house_id, :as => :radio_buttons, :collection => current_user.houses.all.map{|h| [h.address, h.id]}%>
</div>
<%= f.input :first_name %>
<%= f.input :last_name %>
<div class="field">
<%= label_tag :card_number, "Credit Card Number" %>
<%= text_field_tag :card_number, nil, name: nil %>
</div>
<div class="field">
<%= label_tag :card_code, "Security Code on Card (CVV)" %>
<%= text_field_tag :card_code, nil, name: nil %>
</div>
<div class="field">
<%= label_tag :card_month, "Card Expiration" %>
<%= select_month nil, {add_month_numbers: true}, {name: nil, id: "card_month"} %>
<%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+15}, {name: nil, id: "card_year"} %>
</div>
</div>
<div id="stripe_error">
<noscript>JavaScript is not enabled and is required for this form. First enable it in your web browser settings.</noscript>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
Orders.js.coffee
jQuery ->
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'))
order.setupForm()
order =
setupForm: ->
$('#new_order').submit ->
$('input[type=submit]').attr('disabled', true)
if $('#card_number').length
order.processCard()
false
else
true
processCard: ->
card =
number: $('#card_number').val()
cvc: $('#card_code').val()
expMonth: $('#card_month').val()
expYear: $('#card_year').val()
Stripe.createToken(card, order.handleStripeResponse)
handleStripeResponse: (status, response) ->
if status == 200
$('#order_stripe_card_token').val(response.id)
$('#new_order')[0].submit()
else
$('#stripe_error').text(response.error.message)
$('input[type=submit]').attr('disabled', false)
You still need to include :stripe_card_token under attr_accessible in your model
Active record (the layer in your rails stack that provides an interface between your ruby code and your database) protects your database from unwanted end-user assignment using the attr_accessible method. if present in your model it makes sure that a request can't write to your database unless the attribute is listed.
You've got attr_accessible here but don't have :stripe_card_token listed, so you can't save to that field.
attr_accessible :first_name, :last_name, :ip_address, :cart_id, :house_id add , :stripe_card_token
You may have though the attr_accessor :stripe_card_token line would somehow be related, but that just sets the getter and setter methods for the attribute.
The difference is better laid out here
In this question
You can read more about mass-assignment here: http://www.h-online.com/security/news/item/Rails-3-2-3-makes-mass-assignment-change-1498547.html

Connection between three controllers rails

I have three controllers - users, stories, categories. I want when I am logged in as a administrator to create categories and then to write stories for every category. I did part of the task but in DB in table Stories category_id is empty and I cannot understand how to fix it. Here is part of my code:
stories/new.html.erb:
<%= form_for(#story) do |f| %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :category %><br />
<%= f.collection_select :category_id, #categories, :id, :title %>
</div>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit "Add" %>
</div>
<% end %>
stories_controller.rb:
class StoriesController < ApplicationController
before_filter :admin_user
def new
#story = Story.new
#categories = Category.all
#title = "Add news"
end
def create
#categories = Category.all
#story = current_user.stories.build(params[:story])
if #story.save
flash[:success] = "Successfullly added news"
redirect_to #story
else
#title = "Add news"
render 'new'
end
end
private
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
story.rb:
class Story < ActiveRecord::Base
attr_accessible :title, :content
belongs_to :user
belongs_to :category
validates :title, :presence => true
validates :content, :presence => true
validates :user_id, :presence => true
default_scope :order => 'stories.created_at DESC'
end
category.rb:
class Category < ActiveRecord::Base
attr_accessible :title
has_many :stories
validates :title, :presence => true,
:length => { :within => 6..40 }
end
user.rb:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
has_many :stories
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => true
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
def self.authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
(user && user.salt == cookie_salt) ? user : nil
end
private
def encrypt_password
self.salt = make_salt unless has_password?(password)
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
You need add attr_accessible :category_id to your story model
Its preventing mass assignment in your controllers create method. Alternatively you could pull out the category_id from your params hash and assign it on a separate line.

Resources