TIL from the Rocketship

TILJanuary 23, 2019by Andrey Morozov

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:

  1. Register on ngrok

  2. Download ngrok from site

  3. Connect your account

  4. 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 💎

TILJanuary 22, 2019by Alexander Blinov

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;

}}

TILJanuary 21, 2019by Suhomozgy Andrey

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.

TILJanuary 21, 2019by Dmitry Voronov

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

TILJanuary 16, 2019by Dmitry Voronov

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')

TILJanuary 12, 2019by Dmitry Voronov

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
TILJanuary 07, 2019by Dmitry Voronov

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

TILDecember 28, 2018by Alexander Spitsyn

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.