Sunday, March 21, 2021

Add a React component to a Rails application

Why not sprinkle your Rails application with React components?

Prerequisites

  • Upgrade to Rails 6.x.x
  • Be using Webpacker in your application
  • Turbolinks

Install React &c.

In your project directory:

bin/rails webpacker:install:react

This will install react react-dom and some Babel packages with yarn. The Babel config and webpacker yml files are updated. (A sample hello world is also created under javascript/packs - should delete this upon studying)

Create a new pack for your component

Under

javascript/packs
name a file after the component you are using e.g chonky.js

The basic demo for Chonky looks like this in the new pack file:

// chonky.js

import * as React from 'react'
import { render } from 'react-dom'
import { setChonkyDefaults } from 'chonky'
import { ChonkyIconFA } from 'chonky-icon-fontawesome'
import { FullFileBrowser } from 'chonky'

setChonkyDefaults({ iconComponent: ChonkyIconFA })

document.addEventListener("turbolinks:load", (e) => {
  const el = document.getElementById("chonky-browser")
  if (el) {
    const files = [
          { id: 'lht', name: 'Projects', isDir: true },
          {
              id: 'mcd',
              name: 'chonky-sphere-v2.png',
              thumbnailUrl: 'https://chonky.io/chonky-sphere-v2.png',
          },
      ]
    const folderChain = [{ id: 'xcv', name: 'Demo', isDir: true }]
    render(<FullFileBrowser files={files} folderChain={folderChain} />, el)
  }
})
 

Add pack tag to application head

Within the head tags of

application.html.erb
add
<%= javascript_pack_tag 'chonky', 'data-turbolinks-track': 'reload' %>

Add element to HTML page passed to render function

<div id="chonky-browser"></div>



Monday, March 01, 2021

More social buttons

<ul style="padding:0">
  <li>
    <button onclick="window.open('https://twitter.com/intent/tweet?url=' + encodeURIComponent(document.URL) + '&text=' + encodeURIComponent(document.title) + '&via=YOUR_HANDLE', '_blank', 'width=550,height=420')" style="border:0;background:none;padding:0;width:45px;text-align:center;cursor: pointer;"><img src="https://cdn.cms-twdigitalassets.com/content/dam/developer-twitter/images/Twitter_logo_blue_48.png" alt="Twitter" height="48"/></button>
  </li>
  <li>
    <button onclick="window.open('https://www.minds.com/newsfeed/subscribed?intentUrl=' + encodeURIComponent(document.URL), '_blank', 'width=550,height=420')" style="border:0;background:none;padding:0;width:45px;text-align:center;cursor: pointer;"><img src="https://cdn-assets.minds.com/front/dist/en/assets/logos/bulb.svg" alt="Minds" height="48"/></button>
  </li>
</ul> 

Monday, January 09, 2017

CarrierWave and Prawn: how to blacklist png images with Adam7 interlacing

The Ruby on Rails Prawn pdf library does not support rendering of png images with Adam7 interlacing. So you have a few options. One is to convert the image into a format acceptable to Prawn. The other is to blacklist that kind of png. The CarrierWave file uploader library is fairly popular.
 class CustomUploader < CarrierWave::Uploader::Base  
 ...  
 before :cache, :invalidate_adam7  
 ...  
  # See https://github.com/prawnpdf/prawn/blob/ec9531b6eb0a61cfed0748a395679f053a2d62cd/lib/prawn/images/png.rb   
  # and https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/carrierwave/uploader/content_type_blacklist.rb  
  # we invalidate png with interlace method Adam7 as it raises an error in pdf generation via Prawn  
  def invalidate_adam7(new_file)  
   content_type = new_file.content_type  
   return unless content_type == 'image/png'  
   strio = StringIO.new(new_file.read)  
   # set the strio to the start  
   strio.rewind  
   strio.read(8)  
   loop do  
    break if strio.eof?   
    chunk_size = strio.read(4).unpack("N")[0]  
    section = strio.read(4)  
    case section  
    when 'IHDR'  
     values = strio.read(chunk_size).unpack("NNCCCCC")  
     @interlace_method = values[6]  
    when 'IEND'  
     break  
    else  
     strio.seek(strio.pos + chunk_size)  
    end  
   end  
   strio.read(4)  
   if @interlace_method != 0  
    raise CarrierWave::IntegrityError, I18n.translate(:"carrierwave.errors.messages.adam7_error")  
   end  
  end  
 end  

Sunday, May 15, 2016

Build your own social media sharing buttons

I cobbled together a couple of share buttons for Facebook and Twitter for a WordPress site. I'm, obviously (as I still use the Blogger platform), not the biggest fan of WordPress - although it is huge, so... anyway. The idea was to get some consistent looking buttons into some part of a theme. I put the following into a 'Text widget', which allows you to inject arbitrary Text or HTML within some container within a theme:


So in order to use the above you are going to need to sign up for the Facebook apps service and if you want to prepopulate your Twitter handle, you will need your Twitter username as well.

It looks something like this:


Wednesday, May 11, 2016

How to create a watermark in Prawn (the Ruby library for writing PDFs)

 Prawn::Stamp is no good since, since you have to specifically call it for each instance of use, and you won't necessarily know how many pages you are going to get if generating the PDF dynamically from user generated content.

If you use #repeat, this method is going to write over existing text/drawings towards the end of PDF generation. But a watermark should be in the background, not 'stamped' on top.

Here's the solution:

  def watermark(watermark)
    function = lambda {@pdf.formatted_text_box([{:text => "#{watermark}", :color => '5B5B5B', :size => 60}], :rotate => 30, :rotate_around => :center, :align => :center, :valign => :center)}
    # call the function for the first page
    function.call
    # call the function for every subsequent page that is created
    @pdf.on_page_create {function.call}
  end


The method lets you use any string and centres it in the middle of the page irrespective of length.

Calling the function with #on_page_create ensures that the watermark is drawn first; other objects are then drawn on top.

Now watermark("Draft"), for example, can be used in any Prawn::Document.

Monday, February 01, 2016

Creating and manipulating pdfs in the Ruby on Rails framework

Whatever ones feelings on pdf, many people still find security in it as a replacement for paper. Pdf is widely used to store electronic copies of physical documents and transmit electronic flyers and brochures. Many people still print documents instead of keeping them on screen. Often, the file being printed will be in pdf. Pdf files continue to be relevant.

What sorts of things do we want to do with pdf? Here are some ideas:

  1. create reports
  2. replace html views with a pdf option (for ease of printing)
  3. concatenate pdfs
The following goes into limited detail on libraries available for a Ruby on Rails application.

1. Create reports
Prawn does the job. It is possible to stay DRY by collecting common elements to your reports such as a header, footer, divider or currency display helper in one file stored as a class e.g. ReportElements. You can then instantiate a new object taking from this class and use the methods contained within. If you need to change your header, then you only need to change one file.

2. Replace html views with a pdf option
Wicked PDF does this. If you want a higher level of control, then Prawn is a better option for generating reports. This way you can leave web pages to be more interactive and reports to be static. A good use case for Wicked PDF is a website' terms of service and other legal documentation. It's important to have only one file where the terms of service are updated to avoid spotting the difference. Wicked PDF parses the html and dynamically generates a pdf based on it.

3. Concatenate pdfs
CombinePDF seems to be a good option.

Putting it all together in a controller you could do something like this:

def complicated_pdf  
  @model = Model.find params[:model_id]  
  respond_to do |format|  
    format.pdf do  
      # get Prawn document  
      pdf_1_data = ExamplePrawnPdf.new(@model, current_user, view_context).render  
      pdf_1 = CombinePDF.parse(pdf_1_data)  
      # get the terms of service through wkhtmltopdf shell utility and WickedPdf gem  
      pdf_2_data = render_to_string pdf: "", file: "#{Rails.root}/app/views/home/terms_of_service.erb", encoding: "UTF-8"  
      pdf_2 = CombinePDF.parse(pdf_2_data)  
      combined_pdf = CombinePDF.new  
      combined_pdf << pdf_1  
      combined_pdf << pdf_2  
      send_data combined_pdf.to_pdf,  
        filename: "Complicated_report_#{@model.some_unique_identifier}.pdf",  
        type: "application/pdf",  
        disposition: "inline"  
    end  
  end  
end
  

Thursday, January 29, 2015

NGINX Basic Authorization

 
 I wanted to quickly restrict access to a web domain running on NGINX. An easy way to do this is:

1. find your configuration file often in a subdirectory of /etc/nginx/ for me the path was /etc/nginx/conf.d/local.conf

2. create a file called .htpasswd in the same (could be elsewhere) directory and follow the instructions here as to what to put in it - don't bother with the .htaccess file.

3. add the following lines to your NGINX configuration file and read about them here - you only add a location to pinpoint restricted areas (without means blanket authorization required):

    auth_basic           "closed site";
    auth_basic_user_file conf/htpasswd; 

Wednesday, January 28, 2015

Hot Chicken Salad

Marinate chicken breast in:
soy sauce (sparingly used!)
Sambal Olek
Teaspoon of honey
Garlic

Prepare Salad

Roughly chop:
Basil
Roquet
Mint

Finely chop:
Chilli
Lemon Grass

Crushed clove garlic
Quartered (or divided further if big) tomato
Squeeze half a lime over the top

Cook chicken in hot oil on griddle. Add to Salad.

Saturday, December 13, 2014

libGL error: failed to load driver: swrast

This error occurred for me running Ubuntu 14.04 LTS with nvidia-331-updates proprietary driver.

SOLUTION

when you run the command  

ldconfig -p | grep libGL.so

you want the output to look like this:

    libGL.so.1 (libc6,x86-64) => /usr/lib/nvidia-331-updates/libGL.so.1
    libGL.so.1 (libc6) => /usr/lib32/nvidia-331-updates/libGL.so.1
    libGL.so (libc6,x86-64) => /usr/lib/nvidia-331-updates/libGL.so
    libGL.so (libc6) => /usr/lib32/nvidia-331-updates/libGL.so


but it doesn't.

go to

cd /etc/ld.so.conf.d

list all of your files in there

ls

check each file

nano filename.conf 

if the file has a line pointing to "/usr/lib/i386-linux-gnu/mesa" replace with "/usr/lib32" or delete the file if another file in the directory already has this then:

sudo ldconfig 

and check the output of

ldconfig -p | grep libGL.so 

attribution: http://askubuntu.com/questions/313173/why-are-my-32bit-opengl-libraries-pointing-to-mesa-instead-of-nvidia-and-how-do

Tuesday, November 25, 2014

Web scraping databases with Ruby

Web scraping databases with Ruby

http://static.guim.co.uk/sys-images/Technology/Pix/pictures/2009/8/25/1251203143449/Galileos-telescope-001.jpg

Introduction

This post is more of an overview with issues that I have encountered and my way of approaching accumulating data records from websites than a technical how-to. For technical information read the documentation and consult stackoverflow or other forums. I do provide a neat way to turn your json records into csv courtesy of an unattributed forum post (if I find it again, I will attribute).

Ingredients

  1. Ruby 2.1 or up;
  2. mechanize for Ruby;
  3. nokogiri;
  4. json gem. 
Alternatively to point 2. above you will need the following bundle:
  1. watir
  2. watir-webdriver
  3. headless

Method

  1. Examine your target database - you can usually search by some identifier number. What range of numbers does the database employ? Does it count up from 1 or 1000? Are there series of numbers associated with a type of record as part of the set you are acquiring (e.g. individual or corporation)? Does the range have zeros prepended?
  2. Determine the range of numbers you wish to search.
  3. Divide this up into manageable sub-ranges if the numbers exceed say 10,000 queries. Or if there are 20 million queries, perhaps you do 20 sub-ranges of a million each.
  4. Test getting to a sample target record in irb with mechanize first. Can you successfully submit the form? Save the result and examine what appears in your browser.
  5. If you can get to the target record with mechanize then you have two options: save the html for each record and post-process; or process on the fly. Either way you will be using nokogiri and xpath selectors.
  6. If you can't figure it out with mechanize (usually because there is a whole pile of javascript going on), then you will have to use the webdriver headless option: start off without headless and test with irb. You may well have to process on the fly, so monitor your records as they come through and restart if something is amiss.
  7. Process your data with nokogiri and xpath selectors. Using Firebug will help you visualise where things are. Remember: nokogiri structures everything into an onion of associative arrays (hashes).
  8. Changing the xml tags with nokogiri can sometimes be the silver bullet to help you solve a problem of logic in extracting data!
  9. Conditional statements are your friend. There may be 5 potential rows of data but only four, three or two appear in various records in your range. Can you use conditional statements about label text or distinguishing tags to help you?
  10. Save your records as one record per file by the number you searched for in json format as a hash mimicking the website record. You can convert this to csv later for presentation in spreadsheets with the following:
require 'json'
require 'csv'
ary = Array.new

Dir.foreach("data") do |f|
  next if f == '.' or f == '..'
  # put the file into the hash
  record_json = File.open("data#{f}", "r").read
  record = JSON.parse(record_json)

  ary << record 
end

CSV.open("data/data.csv", "wb") do |csv|
  csv << ary.first.keys
  ary.each do |hash|
    csv << hash.values
  end
end



Wednesday, February 19, 2014

Chilli Chicken Variation # 3 #paleo

Base:
Butter, onion, sambal oeleck, mustard powder, ground coriander.
"Simmer" on low heat as if to caramelise. Add boiling water as required to prevent burning.

Chicken:
Cut strips of thigh and coat in coconut flour.

Cook:
Braise chicken in base - still on low heat. Add very small amounts of boiling water if required. Add small splashes of maple syrup after chicken is mostly cooked. Then continue to braise for a bit longer than you would expect due to low heat. Chicken juices should eventually moisten the base.

Serve:
Boiled baby carrots and baby corn between roquet garnish around edge of plate.

Wednesday, December 04, 2013

Fruit tipple

Fresh Orange Juice
Kiwi Fruit
Rock Melon

In the blender.

Add white rum and ice, makes its own daiquiri!

Sunday, December 01, 2013

Summer has arrived!

And to celebrate I'm posting an old classic:
Prosciutto
Rock Melon
Tooth picks

Wrap the prosciutto around the rock melon and put a tooth pick in it. How easy is that!?

Thursday, November 07, 2013

Another gluten-free dairy-free biscuit - coconut flavoured

1 cup desiccated coconut
1 cup gluten-free all purpose plain flour
3/4 cup caster sugar
1/4 cup icing sugar
Pinch of gluten-free baking powder
60g melted copha
2 beaten eggs
Generous splash of coconut essence
Hint of vanilla essence

Amounts above may be innaccurate.

Bake at 170 C fanforced for 20 mins

Saturday, November 02, 2013

Variation allegretto on Stephanie Alexander's slow-roasted loin of pork with garlic, rosemary and fennel

We had fennel, celery and some starting-to-grow-old granny smith apples from my recently commenced participation in an organic fruit and vegetable bulk buy and split it up type arrangement.

I followed the recipe in Stephenie Alexander's the cook's companion except as follows:

1. Cut of Pork was pork rib roast (no ribs) aka pork roast aka pork centre loin roast -I ended up cutting it open a bit more to smear the oil mixture over a greater area;
2. Cooked at 250 celsius for 15 minutes, then turned heat down to 200 for further 45 minutes;
3. Absolutely molested the skin with salt to create crackling NOTE: remove crust of salt upon beong cooked;
4. No pork rib bones in the  vegetables;
5. Substitue potatoes with granny smith apples.

Flat 2 late breakfast

Semi poached eggs (2 cms of water in pan and dash of vinegar)
Grilled bacon
Dark rye toast
Avocado butter: parsley, tomato relish whizzed avocado

Saturday, September 14, 2013

Gluten-Free Dairy-Free Strawberry Friands

These friands are gluten and dairy free and taste like cumulus at dawn. It's strawberry season so I used fresh strawberries but you could put any kind of berry in - even something frozen like a frozen raspberry. This recipe makes about twelve in small shallow muffin baking thingo.
You will need:
1.5 cups pure icing sugar
1.5 cups almond meal
0.5 cup gluten free plain flour
3 eggs
180 g Copha
Fresh strawberries
Melt the Copha on the stove and prepare some ice water in a high rimmed baking tray to stand the pan in after the Copha has melted.
While Copha is cooling brush your baking holes with some of the Copha.
Mix sifted icing sugar, almond meal and gluten free plain flower. I cheat and don't sift and use a stick mixer to combine.
Create a well in the dry ingredients and add whisked eggs and cooled Copha.
Mix with a wooden spoon to being with and then bring in the stick mixer.
Fill baking holes to the brim with mixture.
Cut strawberries so as to fit in the centre of the holes. And put in slices of strawberries.
Bake for twenty minutes plus at 180 degrees celsius fan forced.
Allow to stand and dust tops with icng sugar.

Tuesday, September 10, 2013

Salmone al udon e insalata di alghe

Teriyaki Salmon
Udon Noodles
Salad:
Grated carrot
Grated beetroot
Cob lettuce leaves
Alfalfa sprouts
Continental parsley
Yakinori seaweed shredded

Saturday, August 24, 2013

Kevin Rudd Brunch Plate

I thought his breakfast looked comically like Kevin. The only thing missing is his tatty hair cut. What a buffoon!  Anyway the mouth is sirloin pork. The cheeks, eyes and glasses are toast, eggs, salami, amd cherry tomato. The nose is fresh basil (perennial) leaves.

Wednesday, June 26, 2013

Winter's day beach house salad

Freshly baked salmon.
Shallots.
Cherry tomatoes
Continental parsley
Roquet
Spinach
Olive oil and houmous dressing