Brian Gilbank

Joined

9,090 Experience
61 Lessons Completed
3 Questions Solved

Activity

Posted in Iterating using css grid

it's not stopping at 3 when it counts, eg

def index
  @q = Gym.ransack(params[:q]) 
  @gyms = @q.result.includes(:province).page(params[:page]).order("created_at DESC").paginate(:page => params[:page], :per_page => 18)
end

Posted in Iterating using css grid

Hey Chris - Just a follow up on this question. I am seeing a bunch of duplicate entries: https://gympass.ca/fitness-facilities for some reason?

<div class="clubs__index">
      <% @gyms.each_slice(3) do |gyms| %>
       <% @gyms.each_with_index do |gym, index| %>
        <div class="clubs__index--<%= index + 1 %>">
         <%#= render 'gyms/card' %>
         <div class="card">
          <% if gym.image.present? %>
            <%= image_tag gym.image.url, class: 'card__image', alt: gym.gym_name, size: "700X500"  %>
          <% else %>
            <%= image_tag 'https://ficdn-files.ca/images/gympass/logo/default_club_image_index.jpg', class: 'card__image', loading: "lazy" %>
          <% end %>
          <h3 class="card__title"><%= gym.gym_name %></h3>
            <h5 class="card__address">
            <%= gym.gym_address.titleize %><br> 
            <%= gym.gym_city.titleize %> / <%= gym.province.name if gym.province %><br>
            <%= gym.gym_postal.insert(3, ' ').upcase %>
            </h5> 
            <p class="card__details">
            <span class="card__details--bold"><%= t("gyms_index.telephone") %></span>
            <%= number_to_phone(gym.gym_telephone, area_code: true ) %><br>
            <% if gym.ext.present? %>
              <span class="card__details--bold"><%= t("gyms_index.ext") %></span>
              <%= gym.ext  %><br>
              <% end  %>
            <% if gym.gym_email.present? %>
              <span class="card__details--bold"><%= t("gyms_index.email") %></span>
              <%= gym.gym_email.downcase %> 
            <% end %>
          </p>
          <p class="card__url">
            <% if gym.gym_website.present? %>
            <%= link_to url_for(gym.gym_website), target: '_blank' do  %>
            <%= truncate(gym.gym_website.downcase.try(:remove, /.*\/\//), :escape => false, :length => 44)  %>
            <% end %>
          <% end %>
          </p>
          <ul class="card__social">
            <% if gym.gym_facebook.present? %>
            <li class="card__social--items">
              <%= link_to gym.gym_facebook, target: '_blank' do %>
              <i class="card__social--icons fab fa-facebook-f"></i>
              <% end %>
            </li>
            <% end %>
            <% if gym.gym_twitter.present? %>
            <li class="card__social--items">
              <%= link_to gym.gym_twitter , target: '_blank' do %>
              <i class="card__social--icons fab fa-twitter"></i>
              <% end %>
            </li>
            <% end %>
            <% if gym.gym_instagram.present? %>
            <li class="card__social--items">
              <%= link_to gym.gym_instagram, target: '_blank' do %>
              <i class="card__social--icons fab fa-instagram"></i>
              <% end %>
            </li>
            <% end %>
            <% if gym.gym_youtube.present? %>
            <li class="card__social--items">
              <%= link_to gym.gym_youtube, target: '_blank' do %>
              <i class="card__social--icons fab fa-youtube"></i>
              <% end %>
            </li>
            <% end %>
          </ul>
          <% if logged_in? %>
          <ul class="card__admin">
            <li class="card__admin--items">
              <%= link_to 'View', gym, class: "link-padding" %>
            </li>
            <li class="card__admin--items">
              <%= link_to 'Edit', edit_gym_path(gym), class: "link-padding" %>
            </li>
            <li class="card__admin--items">
              <%= link_to 'Destroy', gym, method: :delete, data: { confirm: 'Are you sure?' }, class: "link-padding" %>
            </li>
          </ul>
          <% end %>                                       
        </div> <%# card %>

        </div>
       <% end %> <%# do block end %> 
      <% end %>

    </div>  <%# clubs index %>

Posted in Iterating using css grid

Awesome you saved me! It probably would have taken me a week to figure that out. Thank you.

I wanted to rebuild a site without using a CSS framework. But I will try out Tailwind on my next project.

Posted in Iterating using css grid

Hi Chris - I am using CSS grid for a three-column blog layout. Unfortunately, when I run the do bock it creates three instances of the same post. I guess this is to be expected.

Not sure how to get around this issue with CSS grid. Wondering if you might know of a fix?

I put a basic example below.

.someclass {
 display: grid;
 grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
}

Index page

<% @posts.each do |post| %>

# grid one
<div class="grid-1">
 <%= post.title %>
</div>

# grid two
<div class="grid-2">
 <%= post.title %>
</div>

# grid three
<div class="grid-3">
 <%= post.title %>
</div>

<% end %>

Posted in Tabs with Stimulus

Hahah!

Posted in Tabs with Stimulus

Thanks for the quick response, Chris. I switched computers and forgot that I didn't have any pages created on my desktop database. Embarrassing mistake lol.

Posted in Tabs with Stimulus

Oh never mind I am dumb. Figured it out.

Posted in Tabs with Stimulus

Hi Chris - I've been following your guide on adding tabs with Stimulus here: https://github.com/excid3/tailwindcss-stimulus-components

I am not using Tailwind but everything is working. However, when I add a block inside tab 1 nothing is rendered? The h2 content is rendered though.

There are no errors, so I am scratching my head.

example:

<div class="hidden dbroot__content--1" data-target="tabs.panel">
    <h2>This is tab 1</h2>
    <div class="whatever">
    <% @pages.each do |page| %>
         <%= page.title %>
         <%= page.slug %>
         <%= link_to 'Show', page %>
         <%= link_to 'Edit', edit_dashboard_page_path(page) %>
         <%= link_to 'Destroy', page, method: :delete, data: { confirm: 'Are you sure?' } %>
      </tr>
    <% end %>
    </div>

No affect, unfortunately. No form errors are displayed if I try and update with a blank email or name.

Hey Chris - I have a follow-up question on the above issue if you don't mind.

My current set up allows users to edit their password in the traditional way, but in my dashboard/users controller, I want users to be able to update their profile, i.e. their name, email, bio, etc without a password.

This all works, but if a user fails to enter their email or name, no validation errors are displayed. I believe that Devise considers an empty field as no change, from what I can gather from the terminal output.

Is there a way to show the errors or am I going about this the wrong way?

Users Controller

module Dashboard
  class UsersController < DashboardsController
    before_action :set_user, only: [:show, :edit, :update, :destroy]
    before_action :check_admin, only: [:index]

    layout 'dashboard'

    def index
      @users = User.all
    end

    def show
    end

    def edit
    end

    def update
      if @user.update(user_params) 
       bypass_sign_in(@user)
       redirect_to dashboard_user_path(@user)
       flash[:success] = t('dashboard_pages.users_controller.messages.update_success')
     else
       render :edit
     end
    end

    def destroy
      @user.destroy
      redirect_back(fallback_location: dashboard_root_path)
      flash[:success] = t('dashboard_pages.users_controller.messages.user_deleted')
    end

    private

    def set_user
      @user = User.friendly.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:username, :email, :name, :role, :about, :slug, :img)
    end


  end
end

Form partial

<%= form_with(model: [ :dashboard, @user ]) do |f| %>
<%= render partial: 'shared/form_errors', locals: {obj: @user} %>

Errors partial

<% if obj.errors.any? %>
  <div id="error_explanation">
    <h4 class="white"><%= pluralize(obj.errors.count, "error") %> prohibited this about from being saved:</h2>

    <ul class="devise__errors">
      <% obj.errors.full_messages.each do |message| %>
        <li class="white devise__errors--list"><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

routes

namespace :dashboard do
        root to: 'dashboards#index'
        resources :dashboards, only: [:index]
        resources :users
devise_for :users, path: '', path_names: {edit: 'user_edit', new: 'new', sign_up: 'register', sign_in: 'login', sign_out: 'signed-out', password: 'secret', confirmation: 'verification', unlock: 'unlock', invitation: 'invite', verification: 'verification', remove: 'remove', accept: 'accept' }, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: 'users/registrations', masquerades: 'users/masquerades', invitations: 'users/invitations' }
get 'signed-out', to: 'signouts#index'

Ah I am dumb, I got it working. Forgot that I needed to add the admin module in my form.

<%= form_for ["admin", @user] do |f| %>

Thank you for the quick reply.

Hey Chris - Do you have a best practices guide for server maintenance?
Brian

Posted in Nested Comment Threads in Rails - Part 1 Discussion

Woohoo, thanks for doing this!

Posted in Comments With Polymorphic Associations Discussion

That would be awesome. Thanks!

Posted in Comments With Polymorphic Associations Discussion

It would great if you could expand this to include comment replies. There are no great tutorials on how to accomplish this task.

Posted in Paypal & Stripe

I guess I am just not understanding how to change it from a subscription to a plan.

I am also currently using a developement url not a production one for my webhooks.

irb(main):001:0> e = Stripe::Event.retrieve("evt_1D3oX72OobZT8tZuFcNxXcpk")
=> #<Stripe::Event:0x2b1cb2657800 id=evt_1D3oX72OobZT8tZuFcNxXcpk> JSON: {
  "id": "evt_1D3oX72OobZT8tZuFcNxXcpk",
  "object": "event",  "api_version": "2018-05-21",
  "created": 1535390985,  "data": {"object":{"id":"sub_DUo9TmZzClFG1N","object":"subscription","application_fee_percent":null,"billing":"charge_automatically","billing_cycle_anchor":1535390983,"cancel_at_period_end":false,"canceled_at":null,"created":1535390983,"current_period_end":1538069383,"current_period_start":1535390983,"customer":"cus_DT7O4lor9yOHge","days_until_due":null,"discount":null,"ended_at":null,"items":{"object":"list","data":[{"id":"si_DUo92aCIKDATBh","object":"subscription_item","created":1535390984,"metadata":{},"plan":{"id":"monthly","object":"plan","active":true,"aggregate_usage":null,"amount":1900,"billing_scheme":"per_unit","created":1534660009,"currency":"cad","interval":"month","interval_count":1,"livemode":false,"metadata":{},"nickname":"Monthly","product":"prod_DRdeH2Q4y35faL","tiers":null,"tiers_mode":null,"transform_usage":null,"trial_period_days":null,"usage_type":"licensed"},"quantity":1,"subscription":"sub_DUo9TmZzClFG1N"}],"has_more":false,"total_count":1,"url":"/v1/subscription_items?subscription=sub_DUo9TmZzClFG1N"},"livemode":false,"metadata":{},"plan":{"id":"monthly","object":"plan","active":true,"aggregate_usage":null,"amount":1900,"billing_scheme":"per_unit","created":1534660009,"currency":"cad","interval":"month","interval_count":1,"livemode":false,"metadata":{},"nickname":"Monthly","product":"prod_DRdeH2Q4y35faL","tiers":null,"tiers_mode":null,"transform_usage":null,"trial_period_days":null,"usage_type":"licensed"},"quantity":1,"start":1535390983,"status":"active","tax_percent":null,"trial_end":null,"trial_start":null}},
  "livemode": false,
  "pending_webhooks": 1,
  "request": {"id":"req_jreFxqxCqmXk5P","idempotency_key":null},
  "type": "customer.subscription.created"
}
irb(main):002:0> Webhooks::ChargeSucceeded.new.call(e)
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."stripe_id" = $1 LIMIT $2  [["stripe_id", "cus_DT7O4lor9yOHge"], ["LIMIT", 1]]
  Charge Load (0.2ms)  SELECT  "charges".* FROM "charges" WHERE "charges"."user_id" = $1 AND "charges"."stripe_id" = $2 ORDER BY "charges"."id" ASC LIMIT $3  [["user_id", 9], ["stripe_id", "sub_DUo9TmZzClFG1N"], ["LIMIT", 1]]
   (0.1ms)  BEGIN
  Charge Create (33.9ms)  INSERT INTO "charges" ("user_id", "stripe_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["user_id", 9], ["stripe_id", "sub_DUo9TmZzClFG1N"], ["created_at", "2018-08-27 22:52:08.728957"], ["updated_at", "2018-08-27 22:52:08.728957"]]
   (7.2ms)  COMMIT
Traceback (most recent call last):
        2: from (irb):2
        1: from app/models/webhooks/charge_succeeded.rb:10:in `call'
NoMethodError (undefined method `amount' for #<Stripe::Subscription:0x00005639687c59c8>)

Inside of Stripe

Webhooks
Pending
http://localhost:3000/webhooks/stripe
Status
503 Pending (5 tries)
Retry history
[2018/08/27 11:29 to http://localhost:3000]: (503) ERR
[2018/08/27 12:49 to http://localhost:3000]: (503) ERR
[2018/08/27 14:02 to http://localhost:3000]: (503) ERR
[2018/08/27 15:07 to http://localhost:3000/webhooks/stripe]: (503) ERR
[2018/08/27 15:47 to http://localhost:3000/webhooks/stripe]: (503) ERR
Request
{
  "id": "evt_1D3oX72OobZT8tZuFcNxXcpk",
  "object": "event",
  "api_version": "2018-05-21",
  "created": 1535390985,
  "data": {
    "object": {
      "id": "sub_DUo9TmZzClFG1N",
      "object": "subscription",
      "application_fee_percent": null,
      "billing": "charge_automatically",
      "billing_cycle_anchor": 1535390983,
      "cancel_at_period_end": false,
      "canceled_at": null,
      "created": 1535390983,
      "current_period_end": 1538069383,
      "current_period_start": 1535390983,
      "customer": "cus_DT7O4lor9yOHge",
      "days_until_due": null,
      "discount": null,
      "ended_at": null,
      "items": {
        "object": "list",
        "data": [
          {
            "id": "si_DUo92aCIKDATBh",
            "object": "subscription_item",
            "created": 1535390984,
            "metadata": {
            },
            "plan": {
              "id": "monthly",
              "object": "plan",
              "active": true,
              "aggregate_usage": null,
              "amount": 1900,
              "billing_scheme": "per_unit",
              "created": 1534660009,
              "currency": "cad",
              "interval": "month",
              "interval_count": 1,
              "livemode": false,
              "metadata": {
              },
              "nickname": "Monthly",
              "product": "prod_DRdeH2Q4y35faL",
              "tiers": null,
              "tiers_mode": null,
              "transform_usage": null,
              "trial_period_days": null,
              "usage_type": "licensed"
            },
            "quantity": 1,
            "subscription": "sub_DUo9TmZzClFG1N"
          }
        ],
        "has_more": false,
        "total_count": 1,
        "url": "/v1/subscription_items?subscription=sub_DUo9TmZzClFG1N"
      },
      "livemode": false,
      "metadata": {
      },
      "plan": {
        "id": "monthly",
        "object": "plan",
        "active": true,
        "aggregate_usage": null,
        "amount": 1900,
        "billing_scheme": "per_unit",
        "created": 1534660009,
        "currency": "cad",
        "interval": "month",
        "interval_count": 1,
        "livemode": false,
        "metadata": {
        },
        "nickname": "Monthly",
        "product": "prod_DRdeH2Q4y35faL",
        "tiers": null,
        "tiers_mode": null,
        "transform_usage": null,
        "trial_period_days": null,
        "usage_type": "licensed"
      },
      "quantity": 1,
      "start": 1535390983,
      "status": "active",
      "tax_percent": null,
      "trial_end": null,
      "trial_start": null
    }
  },
  "livemode": false,
  "pending_webhooks": 1,
  "request": {
    "id": "req_jreFxqxCqmXk5P",
    "idempotency_key": null
  },
  "type": "customer.subscription.created"
}
Response
host localhost:3000 resolves to illegal IP 127.0.0.1

Posted in Paypal & Stripe

I am a bit confused. So I would have to ask for the plan first before I can retrieve the amount?
Brian

Posted in Paypal & Stripe

Hi Chris - Can I bug you for one last question? I am getting a no method error for amount in the rails console after I run:
e = Stripe::Event.retrieve("evt_1D29rN2OobZT8tZuHzzw9Zbp")
Webhooks::ChargeSucceeded.new.call(e)

NoMethodError (undefined method `amount' for #Stripe::Subscription:0x00005630b03da5b8)

Amount is set in my database so I am not sure what's going on.

module Webhooks
  class ChargeSucceeded
    def call(event)
      charge = event.data.object

      user = User.find_by(stripe_id: charge.customer)

      c = user.charges.where(stripe_id: charge.id).first_or_create
      c.update(
        amount: charge.amount,
        card_brand: charge.source.brand,
        card_last4: charge.source.last4,
        card_exp_month: charge.source.exp_month,
        card_exp_year: charge.source.exp_year
      )
    end
  end
end
class CreateCharges < ActiveRecord::Migration[5.2]
  def change
    create_table :charges do |t|
      t.references :user, foreign_key: true
      t.string :stripe_id
      t.integer :amount
      t.string :card_brand
      t.string :card_last4
      t.string :card_exp_month
      t.string :card_exp_year

      t.timestamps
    end
  end
end

Here is a dropbox link to my application: https://www.dropbox.com/s/xj9ul4lbj118mh5/stripe.zip?dl=0

Posted in Paypal & Stripe

After some testing and digging around, I figured it out. I think it was due to the fact that I was using form_with instead of form_tag.

Maybe form_with doesn't accept a style tag anymore? My fix was just to change style to class, and then add the none class to my css.

<%= form_with url: subscription_path, id: "payment-form", class: (current_user.card_last4? ? "none" : nil) do |form| %>

Thanks for you help with this.

Posted in Paypal & Stripe

Thanks Chris - I just subscribed, and I will watch that eposide.

One last question if you don't mind. I am on this eposide of your stripe course "Resubscribing using an existing card" but I am having a slight problem.

My form is still displaying for users with an existing credit card on file with this set up:

<%= form_with url: subscription_path, id: "payment-form", style: (current_user.card_last4? ? "display:none" : nil) do |form| %>

Do you know what I am doing wrong here?

Complete file: (Subscriptions.new.html.erb)

<h1>Subscribe</h1>

<% if current_user.card_last4? %>
  <%= form_with url: subscription_path, id: "existing-card" do |form| %>
    <p>Pay with existing card</p> 
    <div><strong>Card on file:</strong> <%= current_user.card_brand %> (**** **** **** <%= current_user.card_last4 %>)</div>
    <div><strong>Expiration:</strong> <%= current_user.card_exp_month %> / <%= current_user.card_exp_year %></div>
    <p>or <%= link_to "Add a new card", "#", class: "show-card-form" %></p>

    <%= hidden_field_tag :plan, params[:plan] %>
    <button class="btn btn-primary">Submit Payment</button>
 <% end %>
<% end %>

<%= form_with url: subscription_path, id: "payment-form", style: (current_user.card_last4? ? "display:none" : nil) do |form| %>

  <div class="form-row">
    <label for="card-element">
      Credit or debit card
    </label>
    <div id="card-element" class="form-control">
      <!-- A Stripe Element will be inserted here. -->
    </div>

    <!-- Used to display Element errors. -->
    <div id="card-errors" role="alert"></div>
  </div>

  <%= hidden_field_tag :plan, params[:plan] %>

  <button class="btn btn-primary">Submit Payment</button>


<% end %>