Taylor Cooney

Joined

2,050 Experience
2 Lessons Completed
1 Question Solved

Activity

Just updated the bottom of the thread Casey; needing some guidance on setting up the Conversation and Message between two different models.

I have a Store object that has an email_address attribute. Using the logic from and How To Build A Form and Handling Inbound Email Parsing with Rails, I'm trying to figure out how to structure a Conversation where a visitor can email the Store, and the Store can reply through email - their replies would post messages to the Conversation.

When a visitor inquires to the store (via form), I create a Reservation record with their name and email, and start a Conversation like this,
@conversation = Conversation.create(sender_id: self.id, recipient_id: self.store_id)

I wanted to model the notifications similar to this, where everyone but the sender receives an email, but I'm stumped on how to map the User, since it's two different objects (Reservation and Store):

def send_notifications!
    (forum_thread.users.uniq - [user]).each do |user|
      UserMailer.new_post(user, self).deliver_now
    end
  end

The Conversation model looks like this, may be wrong, any guidance on what I could use to make the messages unique and structure the notifications?

class Conversation < ApplicationRecord
  belongs_to :sender, :foreign_key => :sender_id, class_name: "Reservation"
  belongs_to :recipient, :foreign_key => :recipient_id, class_name: "Store"
  belongs_to :reservation

  has_many :messages, dependent: :destroy
end

The Message Post isn't fully fleshed out, not sure what to use to make it unique between the two Objects.

class Message < ApplicationRecord
  belongs_to :conversation
  validates_presence_of :body, :conversation_id

  def send_notifications!

  end
end

Posted in User Onboarding Progress Bar Discussion

Thanks for making this one Chris!

Posted in Vue.js Trello Clone in Rails - Part 8 Discussion

I'm having this problem too with a heroku deploy; anytime I refresh the page the cards disappear dispite the JSON data being rendered.

Posted in How to monitor account progress?

Is there any ETA on an episode like this?

Posted in User Referral Program From Scratch Discussion

You're correct @Monroe...I was getting an error with before_create claiming that the referral code already existed

Posted in How to monitor account progress?

Interested

Posted in How do I upload files with ActiveStorage and Vue.js?

Still looking for some assistance here 😅

Posted in How do I upload files with ActiveStorage and Vue.js?

Has anyone had any success with Vue and ActiveStorage in the past? I could really use some assistance in refactoring the form to handle file uploads.

Posted in How do I upload files with ActiveStorage and Vue.js?

Hey GoRailers,

Stemming from the Rails & Vue.js Trello Clone, I'm looking to add file uploads to a Card model using the new Rails 5.2 ActiveStorage feature. I'm stronger in Rails - previously I would use Paperclip to handle this. I've setup the config/storage.yml file and necessary migrations for ActiveStorage. I've also set the association between on the Card and updated the CardController to permit files: [].

The card.vue component built from the tutorial currently works well; a Card can have a title and description, and the description can be updated. These records persist to the database. My problem is figuring out how to bind file uploads to the card, and the logic required to upload multiple files on save.

Currently I'm using <input name="files" type="file" data-direct-upload-url="/rails/active_storage/direct_uploads" direct_upload="true" /> to create the input field in the card. However, after selecting a PDF image from my local machine and clicking save, the logs show that nothing happens [in regards to inserting a new row in active_storage_attachments or creating a blob]. How can I expand the save method to accept the file? Is the input field sufficient to bind the element?

card.rb

class Card < ApplicationRecord
  has_many_attached :files
end

CardController

class CardsController < ApplicationController
  private
    def card_params
      params.require(:card).permit(:list_id, :title, :position, :description, files: [])
    end
end

card.vue

<template>
    <div>
        <div @click="editing=true" class="card card-body">
            <h4>
                {{card.title}}
            </h4>
        </div>

        <div v-if="editing" class="modal-backdrop show"></div>

        <div v-if="editing" @click="closeModal" class="modal show" style="display: block">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <div>
                            <h4>
                                {{card.title}}
                            </h4>
                        </div>
                    </div>
                    <div class="modal-body">
                        <div>
                            <h5>{{card.description}}</h5>
                        </div>
                        <textarea v-model="description" class="form-control"></textarea>
                    </div>
                    <div class="modal-footer">
                        <input name="files" type="file" data-direct-upload-url="/rails/active_storage/direct_uploads" direct_upload="true" />
                        <button @click="save" type="button" class="button button-secondary">Save changes</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>

export default {
    props: ["card", "list"],

    data: function() {
        return {
            editing: false,
            title: this.card.title,
            description: this.card.description,
            files: []
        }
    },

    methods: {
        save: function() {
            var data = new FormData
            data.append("card[title]", this.title)
            data.append("card[description]", this.description)

            Rails.ajax({
                url: `/cards/${this.card.id}`,
                type: "PATCH",
                data: data,
                dataType: "json",
                success: (data) => {
                    const list_index = window.store.lists.findIndex((item) => item.id == this.list.id)
                    const card_index = window.store.lists[list_index].cards.findIndex((item) => item.id == this.card.id)
                    window.store.lists[list_index].cards.splice(card_index, 1, data)

                    this.editing = false
                }
            })
        }
    }
}
</script>

Posted in Using Webhooks with Stripe Discussion

Sorry Chris I forgot to write back to you! I got it working at like 3:30AM lol Stripe docs made it seem that you needed the event payload, the Stripe-Signature header, and the endpoint’s secret to perform the verification, which was what I was writing here about.

I was able to simply add the signing_secret to config/secrets.yml, and in config/initializers/stripe.rb. My first deploy to heroku only added the signing secret to stripe.rb and was throwing an issue.

config/initializers/stripe.rb
 Rails.configuration.stripe = {
:stripe_publishable_key => ENV['STRIPE_PUBLISHABLE_KEY'],
:stripe_secret_key => ENV['STRIPE_SECRET_KEY']
:stripe_secret_key => ENV['STRIPE_SECRET_KEY'],
:stripe_signing_secret => ENV['STRIPE_SIGNING_SECRET']
}

class RecordCharges
def call(event)
....

Posted in Using Webhooks with Stripe Discussion

What is required to add a signature header and endpoint secret? Stripe now requires them as per their v2.0.0 to verify requests with a StripeEvent.signing_secret
Phil gave me a hand over on StackOverFlow. In short, he recommended I consider this approach:
  • Before requesting the job be performed, create the order record with a nil charge_id
  • After the Stripe transaction has been completed in the job, update the order with the returned charge_id



Posted in URGENT! How do I solve the following error?

There are some fixes for this issue here: https://github.com/shakacode/react_on_rails/issues/956

Are any of these helpful to you?
I managed to get Stripe working and processing Jobs, where a user must pay a one-time charge in order to create a Job record. 

After putting the call to `Stripe::Charge.create` in a background job I can't manage to figure out how to pass the `charge.id` from `Stripe::Charge.create` to an Order object.

I planned to move the Order.create call into the sidekiq worker and access the `charge.id` directly, but I can't access the @job within the worker because a stripeToken can't be used more than once. Any idea on how I can still save the `charge.id` to an `Order`? *(separate from the main Job model)*

JobsController

    def create
        ...
    
        if @job.create_with_stripe(params[:stripeToken])
          if @job.save
            Order.create(
              # Can't figure out how to pass the charge.id from StripePaymentJob
              :charge_id    => @charge.id,
              :job_id       => @job.id
            )
          end
          ...
      end

Job Model

    def create_with_stripe(token)
        Stripe.api_key = Rails.application.secrets.stripe_secret_key
    
        if valid?
          StripePaymentJob.perform_later(token, SecureRandom.uuid)
        else
        ...
      end

Stripe Worker

    class StripePaymentJob < ApplicationJob
      queue_as :default
    
      def perform(token, idempotent_key)
        @charge = Stripe::Charge.create({
          ...
        }, { idempotency_key: idempotent_key })
      end
    end


Awesome, 

That helps a bunch! I know I can disable retry support for a particular worker by appending:
sidekiq_options :retry => false # job will be discarded immediately if failed

In my controller, I create an Order object with the :stripe_token and :job_id if @job.save. If I add the :charge_id to the Order as well, will that be sufficient or is there more I can do? (See the code block in the main post)
Thanks for the response eminkel. In regards to processing the job in a background worker, can I enqueue the job just as I would any other - is there anything special that I need to do? If I understand correctly, I can just skip the ajax call made to check the status of the payment, as customers expect there to be processed at some point.
Bump...I feel this is important to processing charges in a logical manner
Thanks for the reply @jack...I'm using Charge objects. How did you (or would I) go about setting the Service objects with Sidekiq to process in the background? I'm familiar with how Sidekiq and background jobs work, but I ask because the examples online show you having to poll the connection, which is over my head.

Moreover, how are you setting the idempotency_key? Is it simple as setting it in the Object and it automagically works?

Chris, let me know if you have any insight on this with steps that I can implement.

One thing I don't want to have to deal with are refunds because a customer happened to get charged twice. I want to ensure that I've designed my Rails app to be robust in the face of failure before I release it to production.

What is the best way to process Charge requests in the background? Currently I'm using sidekiq to invoke similar jobs (see Mailer example below). Moreover, should I be making idempotent requests? What's the best way to approach this? This way I don't have to worry about creating multiple charges, and also don't start blocking up the server with Charge requests.

Sidekiq Example
def send_job_post_email
JobMailer.send_job_post_email(user).deliver_later
end

JobsController < ApplicationController
def create
@job = current_user.jobs.build jobs_params.merge(stripe_token: stripe_params["stripeToken"])
if @job.create_with_stripe(params[:stripeToken])
  if @job.save
    @order = Order.create(
      :job_id =&gt; @job.id,
      :stripe_token =&gt; @job.stripe_token
    )
  end
  redirect_to job_order_path(@job, @order), notice: 'Registration was successfully created.'
else
  render_form
end

end


Job < ApplicationRecord
def create_with_stripe(token)
Stripe.api_key = Rails.application.secrets.stripe_secret_key
if valid?
  Stripe::Charge.create(
    :amount =&gt; 999,
    :currency =&gt; "cad",
    :source =&gt; token
  )
else
  false
end

rescue Stripe::CardError, Stripe::InvalidRequestError => e
errors.add :base, "Whoops! We were unable to process your card. #{e.message}"
false
end

logo Created with Sketch.

Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

© 2021 GoRails, LLC. All rights reserved.