TIL from the Rocketship
Webhook integration in development with Ngrok š
All begin when Iām using the Pipedrive (is a sales management tool designed to help small sales teams manage intricate or lengthy sales processes) webhook.
The solution was easy I just create an ngrok tunnel
> ngrok http 3000 Session Status online Account Andrey (Plan: Free) Version 2.2.8 Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding http://b1256cb6.ngrok.io -> localhost:3000 Forwarding https://b1256cb6.ngrok.io -> localhost:3000
and then sent the webhook to the generated address and it works. In local development I can user webhook from another api! It's work š
To use it you need to do 4 steps:
-
Register on ngrok
-
Download ngrok from site
-
Connect your account
-
Run it š
ps
In addition, this service has a ngrok-tunel gem that allows you to fully integrate it with your application. But that's another story š
Reset values in React Final Form
How to reset values in React Final Form after submitting
At first I wrote the following code:
<Form onSubmit={onSubmit} render={({ handleSubmit, form }) => ( <form onSubmit={event => { handleSubmit(event).then(() => { form.reset(); }) }} > ... </form> }/>
If we implement a function like this, we will get an error "Uncaught TypeError: Cannot read property 'then' of undefinedā, when we try to submit invalid form.
Invalid forms ā form with validation errors.
The solution
To avoid this error, we need to place handleSubmit(event) to a variable, and if the variable is not undefined call .then().
onSubmit={(event) => { const promise = handleSubmit(event); promise && promise.then(() => { form.reset(); }) return promise; }}
Zip two arrays repeating values of the smaller one
So, we have two arrays and we need map each element of the first array to an element from the second. The length of the second array may be less or equal than the first one. Whatās if we need to repeat values of the smaller one?
For example, there are two arrays:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8] const letters = ['a', 'b', 'c']
And we need to get something like:
[ { number: 1, letter: 'a' }, { number: 2, letter: 'b' }, { number: 3, letter: 'c' }, { number: 4, letter: 'a' }, { number: 5, letter: 'b' }, { number: 6, letter: 'c' }, { number: 7, letter: 'a' }, { number: 8, letter: 'b' } ]
You can use Google or your math skills š and finally will come to this:
const getNormalizedIndex = (index, array) => ((index + array.length) % array.length) % array.length const numbers_letters = numbers.map((item, index) => { return { number: item, letter: letters[getNormalizedIndex(index, letters)] } })
Run it and youāll get the expected result.
Render and combine PDF files into one
Render PDF files from HTML templates in Rails can be done using WickedPDF.
First you need to use ActionView to render HTML to a string:
def render_to_string(data) action_view = ActionView::Base.new action_view.view_paths = ActionController::Base.view_paths action_view.class_eval do include ApplicationHelper include PDFHelper # or other helpers end action_view.render template: 'pdf/template.html', layout: 'layout/pdf.html', locals: { data: data } end
Then you can render pdf with the desired settings like this:
# first pdf file with some view settings and values from data1... pdf1 = WickedPdf.new.pdf_from_string( render_to_string(data1), { pdf: 'report1', page_size: 'Letter', orientation: 'Portrait' }) # ...and second pdf file with some view settings and values from data2 pdf2 = WickedPdf.new.pdf_from_string( render_to_string(data2), { pdf: 'report2', page_size: 'Letter', orientation: 'Landscape' })
And now you can combine it with CombinePDF gem, that provide you parse method to get PDF content and to_pdfmethod to render the result back to PDF.
combiner = CombinePDF.new combiner << CombinePDF.parse(pdf1) combiner << CombinePDF.parse(pdf2) combiner.to_pdf
Use image files from S3 in WickedPdf
If you need to use image files from S3 in your generated pdf file using WickedPdf, then you need first to download the image. You can create method that does this and add it to the helper.
require 'open-uri' module PdfHelper def embed_remote_image(url, content_type) asset = open(url, "r:UTF-8", &:read) base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "") "data:#{content_type};base64,#{Rack::Utils.escape(base64)}" end end
And use image_tag instead of wicked_pdf_image_tag
= image_tag embed_remote_image(file.logo_url, 'image/jpeg')
Manage Elixir versions like RVM & Rbenv
You can install different versions of the Elixir with help of Kiex, like in Ruby with a RVM and Rbenv.
Download and install Kiex
\curl -sSL https://raw.githubusercontent.com/taylor/kiex/master/install | bash -s ```bash In .bashrc (or .zshrc if you use z shell), add the following ```bash [[ -s "$HOME/.kiex/scripts/kiex" ]] && source "$HOME/.kiex/scripts/kiex" ```bash Install required Elixir ```bash kiex install 1.7 # or another version ```bash And then you can use any version ```bash kiex use 1.7 ```bash
How to create zip files on the fly w/o Tempfile
There are many articles about how to archive files from the server and send a zip-file to a client without persisting it on the server. But usually they don't literally do it, because they use temporary files.
There is a simple way to do it without creating any file though. You just have to put files directly to Zip::OutputStream and then read from it. Btw pay attention: you must rewind the stream before reading it.
# some files objects def download(files) zip_stream = Zip::OutputStream.write_buffer do |zip| files.each.with_index(1) do |file, index| # file name must be uniq in archive zip.put_next_entry("#{file.name}--#{index}.#{file.extension}") zip.write(file.read.force_encoding('utf-8')) end end # important - rewind the steam zip_stream.rewind send_data zip_stream.read, type: 'application/zip', disposition: 'attachment', filename: 'files-archive.zip' end
How to implement inheritance in Grape resources?
Grape uses specific DSL to define endpoints in API, thatās why you canāt use base classā instance methods in descendant resources. But thereās one trick:
Thereās an #inherited class method in Ruby which is triggered every time some class inherits from ancestor class. It passes one argument - descendant class. Calling descendantās #instance_eval method we can place any useful stuff inside a block: methods, helpers, before-doās, etc, in this way evaluating it in context of subclass.
class Base < Grape::API def self.inherited(subclass) super subclass.instance_eval do helpers do def current_user @current_user ||= User.find(params[:user_id]) end end # ... end end end class DocumentsResource < Base post '/documents' do @document = current_user.documents.build # ... end end
Note that itās not real inheritance because Base class has not methods defined inside subclass#instance_eval block.