I have stripe subscriptions on my site. yesterday i created a basic way for the user to view their payment history and cards used. i now want to break this up into two views so that the payment history is on one page and the card(s) used is on another page. I feel like i must be missing something but when i tried to do this nothing is showing on the page. I am pretty new to rails so the whole stripe subscriptions thing has been a bit of a challenge but getting there. This is the original code could someone offer some guidance as to the steps i need to follow. I tried literally splitting this into two different view file but that didn't work so i have gone back to where i was originally now!
subcriptions/show.html.erb
<h1>View your payment history and cards</h1>
<h4><%= #charges.data.length %> Charges! </h4>
<% #charges.data.each do |charge| %>
<%= charge.statement_description %>
£<%= charge.amount %>
<%= Time.at(charge.created).strftime("%d/%m/%y") %>
<% end %>
<h4><%= #charges.data.length %> Cards! </h4>
<% #cards.each do |card| %>
<%= card.brand %>
**** **** **** <%= card.last4 %>
<%= card.exp_month %>/<%= card.exp_year %>
<% end %>
subscriptions controller:
class SubscriptionsController < ApplicationController
def new
#subscription = Subscription.new
end
def create
# raise 'a'
#subscription = Subscription.new(params[:subscription].permit(:stripe_card_token))
#subscription.user = current_user
if #subscription.save_with_payment(params[:plan]) #(current_user)
redirect_to #subscription, :notice => "Thank you for subscribing!"
else
render :new
end
end
def destroy
end
def show
customer_token = current_user.subscription.try(:stripe_customer_token)
#charges = customer_token ? Stripe::Charge.all(customer: customer_token) : []
#cards = customer_token ? Stripe::Customer.retrieve(customer_token).cards : []
end
end
subscriptions model
class Subscription < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id #it should be always compulsary
# attr_accessible :plan
attr_accessor :stripe_card_token
def save_with_payment(plan)
if valid?
customer = Stripe::Customer.create(description: 'subscription', plan: plan, card: stripe_card_token)
self.stripe_customer_token = customer.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
end
The most simple way to achieve it, is to create 2 methods in controller (for example payments and cards):
def payments
customer_token = current_user.subscription.try(:stripe_customer_token)
#charges = customer_token ? Stripe::Charge.all(customer: customer_token) : []
end
def cards
customer_token = current_user.subscription.try(:stripe_customer_token)
#cards = customer_token ? Stripe::Customer.retrieve(customer_token).cards : []
end
create 2 views for these (payments.html.erb and cards.html.erb under /views/subscriptions/ directory):
payments.html.erb:
<h1>View your payment history</h1>
<h4><%= #charges.data.length %> Charges! </h4>
<% #charges.data.each do |charge| %>
<%= charge.statement_description %>
£<%= charge.amount %>
<%= Time.at(charge.created).strftime("%d/%m/%y") %>
<% end %>
cards.html.erb:
<h1>View your cards</h1>
<h4><%= #charges.data.length %> Cards! </h4>
<% #cards.each do |card| %>
<%= card.brand %>
**** **** **** <%= card.last4 %>
<%= card.exp_month %>/<%= card.exp_year %>
<% end %>
and edit routes by:
resources :subscriptions do
get '/payments', to: 'subscriptions#payments' # or get 'subscriptions/payments', to: 'subscriptions#payments' (depending on which link you would like to see)
get '/cards', to: 'subscriptions#cards'
end
This would be pretty much it. Run rake routes to see generated routes to use it correctly and you're good to go. Please check for spelling mistakes in case you'll copy something - not sure if I didn't make any.
Related
I cannot solve the following problem
Any help or suggestion is appreciated
Agent collects orders for Product on a paper going from one Shop to another in his Region.
At the end of the day, he inserts the quantity of each Product to each Shop.
Each Shop orders more than 4 types of products.
I need to design to be able to bulk insert.
<table>
<tr>
<td>Shop1</td>
<td>Product1</td>
<td>Product2</td>
<td>Product3</td>
<td>Product4</td>
etc...
</tr>
<tr>
<td>Shop2</td>
<td>Product1</td>
<td>Product2</td>
<td>Product3</td>
<td>Product4</td>
etc...
</tr>
etc...
</table>
In the browser it needs to look as below
One approach could be to use a custom form model. This is a pretty good guide on the subject. This may not be perfect, but should get you on the right track at least. All credit to the following (slightly modified for your question) code goes to Sam Slotsky from the blog linked above. Read the post for more detail about each piece.
Here's the custom form model:
class ProductForm
include ActiveModel::Model
attr_accessor :products
def products_attributes=(attributes)
#products ||= []
attributes.each do |i, product_params|
#products.push(Product.new(product_params))
end
end
end
Here's the controller:
class ProductsController < ApplicationController
def new
#product_Form = ProductForm.new(products: [Product.new])
end
def create
#product_form = ProductForm.new(params[:product_form])
if #product_form.save
flash[:notice] = "Created products"
redirect_to root_path
else
flash[:notice] = "There were errors"
render :new
end
end
end
Here's the view:
<div>
<%= form_for #product_form, url: products_path, method: :post do |f| %>
<%= f.fields_for :products do |c| %>
<%= c.text_field :attr_1 %>
<%= c.text_field :attr_2
<%- # ... etc ... %>
<% end %>
<p>
<%= f.submit "Submit" %>
</p>
<% end %>
</div>
Im trying to get all the Posts from the the Users. I'm following using the act_as_follower gem. The User follows a profile model, and Posts belong to a User.
My User model:
acts_as_follower
My Profile model the user follows:
belongs_to :user
acts_as_followable
Post model:
belongs_to :user
My Post Controller.rb:
def follow
#profile = Profile.find(params[:id])
current_user.follow(#profile)
redirect_to :back
end
def unfollow
#profile = Profile.find(params[:id])
current_user.stop_following(#profile)
redirect_to :back
end
Im trying to implement something like this using follows_by_type method provided:
#posts = current_user.follows_by_type('Post').order("created_at DESC")
But the thing is the User follows the Profile model, but here I'm looking for a type 'Post'.
EDIT
In my index controller i'v set up the following:
#favoritePost = Post.where(user_id: current_user.all_follows.pluck(:id))
And in the view iv implemented this:
<% if user_signed_in? %>
<%#favoritePost.each do |post| %>
<%= post.title %>
<% end %>
<% end %>
The gem lets you follow multiple models and follows_by_type('Post') filters the Posts you follow.
What you're looking to do is to return the posts from the users you follow.
Controller
#posts = Post.where(user_id: current_user.all_following.pluck(:id))
View
<% #posts.each do |post| %>
<%= post.title %>
<% end %>
I worked out this solution, might not be the best, but it works as required.
In my controller i set this up:
#following = current_user.all_following
#followposts = #following.each do |f|
f.user.id
end
And in my view I have set this up:
<% #followposts.each do |f| %>
<% f.user.posts.each do |g| %>
<%= g.title %>
<% end %>
<% end %>
Organization and User have a many-to-many relationship through Relationship. There's a joined signup form. The sign up form works in that valid information is saved while if there's invalid information it rolls back everything.
The problem is that the form does not display the error messages for the nested User object. Errors for Organization are displayed, the form correctly re-renders if there are errors for User, but the errors for User are not displayed.
Why are the errors when submitting invalid information for users not displayed? Any help is appreciated.
The signup form/view:
<%= form_for #organization, url: next_url do |f| %>
<%= render partial: 'shared/error_messages', locals: { object: f.object, nested_models: f.object.users } %>
... fields for organization...
<%= f.fields_for :users do |p| %>
...fields for users...
<% end %>
<%= f.submit "Register" %>
<% end %>
The shared error messages partial:
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% if defined?(nested_models) && nested_models.any? %>
<div id="error_explanation">
<ul>
<% nested_models.each do |nested_model| %>
<% if nested_model.errors.any? %>
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<% end %>
</ul>
</div>
<% end %>
The controller method:
def new
#organization = Organization.new
#user = #organization.users.build
end
def create
#organization = Organization.new(new_params.except(:users_attributes))
#organization.transaction do
if #organization.valid?
#organization.save
begin
#user = #organization.users.create!(users_attributes)
#relationship = #organization.relationships.where(user: #user).first
#relationship.update_attributes!(member: true, moderator: true)
rescue
raise ActiveRecord::Rollback
end
end
end
if #organization.persisted?
if #organization.relationships.where('member = ? ', true).any?
#organization.users.where('member = ? ', true).each do |single_user|
single_user.send_activation_email
end
end
flash[:success] = "A confirmation email is sent."
redirect_to root_url
else
#user = #organization.users.build(users_attributes) if #organization.users.blank?
render :new
end
end
The Organization model:
has_many :relationships, dependent: :destroy
has_many :users, through: :relationships, inverse_of: :organizations
accepts_nested_attributes_for :users, :reject_if => :all_blank, :allow_destroy => true
validates_associated :users
The Relationship model:
belongs_to :organization
belongs_to :user
The User model:
has_many :relationships, dependent: :destroy
has_many :organizations, through: :relationships, inverse_of: :users
Update: If I add an additional line to def create as below, it seems to work, i.e., then it does display the error messages. However, then it for some reason doesn't save when valid information is submitted. Any ideas how to deal with that?
def create
#organization = Organization.new(new_params.except(:users_attributes))
#user = #organization.users.new(users_attributes)
#organization.transaction do
...
Maybe try this:
<%= render partial: 'shared/error_messages',
locals: { object: f.object, nested_models: [ #user ] } %>
I guess the call to #organization.users.blank? doesn't work in the way you expected it to do, as the user is not correctly created, because #create! threw an exeption. Rails probably does a check on the database, to see if there are any users now, and thinks there is still nothing in there. So your #organization.users.build(users_attributes) gets called, but this doesn't trigger validation.
In general I would also recommend you the use of a form object (like in the other answer), when creating complex forms, as this clarifies things like that and makes the view more clean.
This is classic use case for form objects. It is convenient from many perpectives (testing, maintainance ...).
For example:
class Forms::Registration
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
def persisted?
false
end
def initialize(attributes = {})
%w(name other_attributes).each do |attribute|
send("#{attribute}=", attributes[attribute])
end
end
validates :name, presence: true
validate do
[user, organization].each do |object|
unless object.valid?
object.errors.each do |key, values|
errors[key] = values
end
end
end
end
def user
#user ||= User.new
end
def organization
#organization ||= Organization.new
end
def save
return false unless valid?
if create_objects
# after saving business logic
else
false
end
end
private
def create_objects
ActiveRecord::Base.transaction do
user.save!
organization.save!
end
rescue
false
end
end
the controller:
class RegistrationsController < ApplicationController
def new
#registration = Forms::Registration.new
end
def create
#registration = Forms::Registration.new params[:registration]
if #registration.save
redirect_to root_path
else
render action: :new
end
end
end
and the view in HAML:
= form_for #registration, url: registrations_path, as: :registration do |f|
= f.error_messages
= f.label :name
= f.text_field :name
= f.submit
It is worth to read more about form objects.
Nested attributes bit me SOOO hard every time I decided it's a good time to use them, and I see you know a bit of what I'm talking about.
Here's a suggestion of a different approach, use a form object instead of nested attributes: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ see under section 3. Extract Form Objects
You can extract your existing validations on the User model into a module and import that, to expand on the solution from the blog:
https://gist.github.com/bbozo/50f8638787d6eb63aff4
With this approach you can make your controller code super-simple and make simple and fast unit tests of the not-so-simple logic that you implemented inside and save yourself from writing integration tests to test out all different possible scenarios.
Also, you might find out that a bunch of the validations in the user model are actually only within the concern of the signup form and that those validations will come and bite in later complex forms, especially if you're importing data from a legacy application where validations weren't so strict, or when you one day add additional validators and make half of your user records invalid for update.
I had a similar problem. everything seemed to work fine, but I was not getting any errors The solution i found is to build the comment in article#show instead of the view:
#article = Article.find(params[:id])
#comment = #article.comments.build(params[:comment])
and in your articles#show don't use #article.comments.build but #comment:
<%= form_for([#article, #comment]) do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<p><%= f.submit %></p>
<% end %>
make sure you build the comment in your comment#create as well (you really have no choice though :P)
I think you need to pass f.object instead of #comment.
In case someone might be looking for a solution to render form errors in a form, try:
f.object.errors["account.address"].present?`
The address is the nested attribute here.
I am getting the following error on my Rails 4.2 application. I'm trying to setup subscriptions with Stripe. A subscription belongs to a business and has_one plan.
On my view I pass the params in the URL:
http://localhost:3000/subscriptions/new?plan_id=2&business_id=1001
After I submit the form I get the error below and my code follows. Forgive me if this is a beginner question.
Subscriptions Controller
class SubscriptionsController < ApplicationController
before_action :set_subscription, only: [:show, :edit, :update, :destroy]
# GET /subscriptions
def index
#subscriptions = Subscription.all
end
# GET /subscriptions/1
def show
end
# GET /subscriptions/new
def new
#subscription = Subscription.new
#plan = Plan.find_by id: params["plan_id"]
#business = Business.find_by id: params["business_id"]
end
# POST /subscriptions
def create
#subscription = Subscription.new subscription_params.merge(email: stripe_params["stripeEmail"],
card_token: stripe_params["stripeToken"])
raise "Please, check subscription errors" unless #subscription.valid?
#subscription.process_payment
#subscription.save
redirect_to #subscription, notice: 'Subscription was successfully created.'
rescue => e
flash[:error] = e.message
render :new
end
private
def stripe_params
params.permit :stripeEmail, :stripeToken
end
# Use callbacks to share common setup or constraints between actions.
def set_subscription
#subscription = Subscription.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def subscription_params
params.require(:subscription).permit(:plan_id, :business_id)
end
end
Subscription Model
class Subscription < ActiveRecord::Base
belongs_to :business
has_one :plan
def process_payment
customer = Stripe::Customer.create email: email,
card: card_token
Stripe::Charge.create customer: customer.id,
amount: plan.price * 100,
description: plan.name,
currency: 'usd'
end
end
Subscription View (new.html.erb)
<%= form_for #subscription do |f| %>
<% if #subscription.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#subscription.errors.count, "error") %>
prohibited this subscription from being saved:
</h2>
<ul>
<% #subscription.errors.full_messages.each do |message| %>
<li>
<%= message %>
</li>
<% end %>
</ul>
</div>
<% end %>
<h1><%= #business.name %></h1>
<div class="field">
<%= f.hidden_field :plan_id, value: #plan.id %>
</div>
<div class="field">
<%= f.hidden_field :business_id, value: #business.id %>
</div>
<div class="actions">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.application.secrets.stripe_publishable_key %>"
data-image="/img/documentation/checkout/marketplace.png"
data-name="Business Name"
data-description="<%= #plan.name %>"
data-amount="<%= #plan.price*100 %>">
</script>
</div>
<% end %>
Plan Model
class Plan < ActiveRecord::Base
belongs_to :subscription
end
Calling render only loads the view for an action, it doesn't run any of the logic in the method behind the action, which is why there is no #plan available when you render :new from the create action.
I figured out the issue was with my association between Plans and Subscriptions. I had Plans belongs_to Subscription when I should have had it the other way around.
class Subscription < ActiveRecord::Base
belongs_to :business
belongs_to :plan
...
I am building an app with voting (a post has many votes, a user has many votes). It was working when I didn't limit the number of votes per user, but after adding a validation, it gets the following error when I try to vote up a post that the same user voted up previously:
>undefined method `model_name' for NilClass:Class
>Extracted source (around line #28):
>25: <% end %>
>26:
>27: <table class="posts" summary="User posts">
>28: <%= render #posts %>
>29: </table>
>Trace:
>app/views/posts/index.html.erb:28:in `_app_views_posts_index_html_erb__978098650233580665_2485618500'
>app/controllers/votes_controller.rb:23:in `create'
It works when voting for a post which the user has not voted up previously. How do I get rails to flash the error when the user has already voted for the post, but then redirect without an error? I'm sure I've just missed something obvious. The validation works when I try to add a vote through the rails console (ie, rails doesn't add the new vote to the database if that user has already voted for it). Thanks -
The related code in vote.rb:
class Vote < ActiveRecord::Base
attr_accessible :user_id, :post_id
validate :only_one_user_per_post
belongs_to :user
belongs_to :post, :counter_cache => true
private
def only_one_user_per_post
if Vote.where("user_id = ? AND post_id = ?", self.user_id, self.post_id).all.any?
errors.add(:user_id, "Can only post once per post!")
end
end
end
the create from VotesController.rb
def create
#vote = Vote.new(params[:vote])
post = params[:vote][:post_id]
uid = params[:vote][:user_id]
if #vote.save
flash[:success] = "You voted for the article"
redirect_to root_path
else
flash[:failure] = "You did not vote"
render 'posts/index'
end
relevant code from index.html.erb:
<% flash.each do |key, value| %>
<div class="flash <%= key %>"><%= value %></div>
<% end %>
<table class="posts" summary="User posts">
<%= render #posts %>
</table>
And here's the relevant snippet from _post.html.erb:
<%= form_for #vote do |f| %>
<div class="right"> <%= image_submit_tag "plus32.png", :size => "16x16", :class => "squareicon" %>
<span class="label success"><%= post.votes_count %></span>
</div>
In your controller, in the else-branch you have the following code:
else
flash[:failure] = "You did not vote"
render 'posts/index'
end
You also will need to retrieve the #posts there to alleviate the error. Or use redirect_to posts_path instead.
Hope this helps.