My First Sinatra Project

I have a wufoo form that I’m using and I want to automatically see the results on a webpage. One of the hangups of this process is that I can’t put any script on the webserver. But what I can do is embed an iframe. So I could download the information that I wanted from my form to another server and then display that page in the iframe. My question was what to run on my second server, which I’m going to call my results server. All it really needed to do was get the results and then show them. So using a ruby on rails app for this seemed to be overkill. After reading about it, sinatra seemed to be the perfect fit. I could still use ruby and just show the results I wanted. This seemed to be the perfect project to test out sinatra.

The only gems that I needed were sinatra and wuparty. Wuparty is a gem specifically written for wufoo. I installed it with gem install and found that it didn’t install correctly. I looked at the source code on git and saw that everything except the /lib/wuparty/* files got installed. If I knew how to use git better, I’d submit a ticket. But it was easy for me to just fix manually.

Anyway, ignoring all the files I need to deploy or set things up, here’s what I needed.

Gemfile

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '>= 2.5.0'

gem 'wuparty' # For using wufoo API
gem 'capistrano' # For deployment
gem 'rake', '12.3.2'
gem 'rack', '2.0.7'
gem 'sinatra', '2.0.5'

config.ru

require 'rubygems'
require 'sinatra'
require File.expand_path '../display_results.rb', __FILE__

run Sinatra::Application

entry.rb (this is where I define which fields are for what)

Entry = Struct.new(:firstname, :lastname, :institution, :attending, :guestfirstname, :guestlastname, :guestattending)

module Entries

end

if __FILE__ == $0
x = Entry.new('George', "Washington", "White House", "Dinner and Talks", "Martha", "Washington", "Dinner").to_h
puts x[:firstname]
end

display_results.rb

require 'rubygems'
require 'sinatra'
require 'wuparty'
require_relative 'entry'

config = YAML.load_file("config/wufoo.yml")
account = config["ACCOUNT"]
api_key = config["API_KEY"]

get '/results.html' do
erb `cat public/results.html`, layout: false
end

post '/results' do
@entries = []
wufoo = WuParty.new(account, api_key)
form = wufoo.form('FormName')
count = form.count.to_i
pageSize = 100
times = (count / pageSize).to_i
(0..times).each do |l|
form.entries(limit: pageSize, pageSize: pageSize, pageStart: (pageSize * l)).each do |e|
@entries << Entry.new(e["Field1"], e["Field2"], e["Field4"], e["Field6"], e["Field215"], e["Field216"], e["Field218"]).to_h
end
end
output = erb :'results.html'
File.open("public/results.html", "w") do |f|
f.puts(output)
end
end

That’s basically it. It’s really simple, but does exactly what I want it to do. And wufoo has webhooks that you can set up. I use those to send a post request each time someone signs up. This post request generates a new results.html file for me. And that’s the file that I put in an iframe on the website where I’m very limited in what I can do. I store a few things in a config file that I don’t want to store in my repo.

This is pretty simple, that I’m pretty sure it would be easy for me to add another get/post combo for another wufoo form when I add it.

This was a fun project and I like being able to set up sinatra to handle simple things like this. Rails would have been overkill here.

cmake

I haven’t been doing much with writing/compiling c programs of late. So I didn’t really notice the move from using make to using cmake for the build environment. We’ve been having some issues at work with this, so I’ve spent a few days trying to learn it. I’m not at expert at this at all, but I have picked up a few things. So I want to document them here.

Here’s a simple hello.c program.

#include <stdio.h>

int main() {
printf("Hello world!\n");
return 0;
}

It can be compiled with:

yo:hello $ gcc hello.c -o hello
yo:hello $ ./hello
Hello world!

How would I do this using cmake?

yo:hello $ mkdir build
yo:hello $ touch CMakeLists.txt
yo:hello $ ll
total 32
-rw-r--r--  1 maryh  staff     0 Apr 24 10:23 CMakeLists.txt
drwxr-xr-x  2 maryh  staff    68 Apr 24 10:23 build
-rwxr-xr-x  1 maryh  staff  8432 Apr 24 10:09 hello
-rw-r--r--  1 maryh  staff    73 Apr 24 10:06 hello.c

The file that cmake looks for is called CMakeLists.txt. Edit this file so it looks like this:

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(hello)
add_executable(hello hello.c)

All this does is check that we’re using at least version 3 of cmake. Not necessary for this, but for some other work programs, I needed that. The project line sets up the project and stores the name in the variable PROJECT_NAME. And if it’s the top-level CMakeLists.txt file, it also stores it in CMAKE_PROJECT_NAME. And the add_executable line adds an executable target (for us called hello) that’s built from the source file(s) (for us we only have hello.c).

That’s all that is needed. It’s nice to make the build directory and run the cmake program there because it makes things much easier to delete all the files if you’re doing various iterations on things.

To compile then, go to the build directory and run:

yo:hello $ cd build
yo:build $ cmake ../
-- The C compiler identification is AppleClang 9.1.0.9020039
-- The CXX compiler identification is AppleClang 9.1.0.9020039
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/maryh/Documents/C/hello/build

Now use make to compile the program.

yo:build $ make
Scanning dependencies of target hello
[ 50%] Building C object CMakeFiles/hello.dir/hello.c.o
[100%] Linking C executable hello
[100%] Built target hello
yo:build $ ll
total 80
-rw-r--r--   1 maryh  staff  13521 Apr 24 10:33 CMakeCache.txt
drwxr-xr-x  15 maryh  staff    510 Apr 24 10:34 CMakeFiles
-rw-r--r--   1 maryh  staff   4751 Apr 24 10:33 Makefile
-rw-r--r--   1 maryh  staff   1373 Apr 24 10:33 cmake_install.cmake
-rwxr-xr-x   1 maryh  staff   8432 Apr 24 10:34 hello
yo:build $ ./hello
Hello world!

This is definitely overkill for such a simple program, but those are the basic steps for more elaborate programs. And the key file to edit is basically CMakeLists.txt.

Ideas

It’s weird. I’ve just spent the past two hours making myself a calendar. I was wondering how to design it and it just popped into my head that I have pictures from the early 2000s to now. Why don’t I use some of my own pictures on my calendar? And then find some quotes I like and put them over the picture. It’s a really simple idea, but I love it. I’m going to get a calendar that has motivational or inspirational quotes and I can look at pictures of things I’ve done which brings back those memories. Totally going to make me happy.

I had wanted to read a book tonight, but I finally got my bluetooth speaker working with my laptop. So I felt that I had to listen to music. I can’t really read when there’s music playing that I can sing along to, so I gave up reading pretty quickly. Then I was thinking that I need to start tracking that I’m eating vegetables, so I need a calendar. And making calendar on the computer is something I can easily do while listening to music. I don’t know why, but now that I don’t have a tv, I feel like I’m getting more and more good ideas for projects. Or maybe it’s just that I have the time to do them, since I’m not sitting in front of a tv. All I know is that I’m loving getting more and more ideas, so I hope they keep coming.

UPDATE: Just printed the calendar. I have a cheap printer and it says it prints on 13×19 paper, which technically it does. But on a few pages, everything was a little skewed. So I think it has a hard time handling paper that big. But it’s printed and hanging on my wall and I love it!

Getting Started with the Google API for Reading Public Calendars

I have a few public calendars at work that we use to show events taking place in various departments. I am creating a digital sign for one of our buildings and I’d like to be able to grab the events for the current day and display them on the digital sign. I’m most familiar with ruby right now, so I’m writing a ruby script to do this.

The first thing is I need to install the ruby google-api-client.

$ gem install google-api-client

Since all of the calendar data that I want to grab is on public calendars, I can use an API key to get all this data. Instructions for generating an API key are here: https://support.google.com/cloud/answer/6158862. You also need the calendarid for the calendars you want to grab data from.

In order to see if I have things set up correctly, the following script will grab all the entries from the calendar specified by the calendarid.

require 'google/apis/calendar_v3'

Google::Apis.logger.level = Logger::DEBUG

calendar = Google::Apis::CalendarV3::CalendarService.new
calendar.key='YOUR_API_KEY'
puts calendar.list_events('GOOGLE_CALENDAR_ID')

$ ruby simple_test.rb
...all calendar events show...

With the key working correctly, how can I limit the results to just a single day? And only see the fields that I’m interested in? For us, these are the summary, description, start date_time and location.

require 'google/apis/calendar_v3'

calendar = Google::Apis::CalendarV3::CalendarService.new
calendar.key='YOUR_API_KEY'

events = calendar.list_events('GOOGLE_CALENDAR_ID',
	always_include_email: false,
	time_min: '2018-04-23T00:00:00-05:00',
 	time_max: '2018-04-23T23:59:59-05:00'
)

events.items.each do |item|
	puts item.summary
	puts item.description
	puts item.start.date_time
	puts item.location
	puts "====="
end

I hope to have this script do a bit more. So I’ve put it on my github page. The repository is here: https://github.com/maryheintz/google-calendar-api-ruby

Root Logins with Key Required

I love the fact that I still have so much to learn. I was thinking that I should set things up so that root can ssh in to a computer but only if they have keys set up. One very quick google search and I had my answer.

[[email protected] ~]# grep PermitRoot /etc/ssh/sshd_config
PermitRootLogin without-password

I had just assumed the settings for PermitRootLogin were yes or no. It’s great that the programmers also had thought of this other use. People are so much smarter than me.

Splitting a PDF File

I had a pdf file that was too large to email, but I needed to email.  So I quickly broke it up into two smaller files.  I used this command:

$ gs -dNOPAUSE -dBATCH -dFirstPage=1 -dLastPage=34 -sDEVICE=pdfwrite -sOutputFile=physics1.pdf -f Physics\ Faculty.pdf 
$ gs -dNOPAUSE -dBATCH -dFirstPage=35 -dLastPage=68 -sDEVICE=pdfwrite -sOutputFile=physics2.pdf -f Physics\ Faculty.pdf 

My New MacBook Pro

As noted, with the new year, I’m starting to use my new MacBook Pro full-time. Last night, it took a few hours to copy everything from my old backup to this new laptop. Did it work perfectly, of course not. Plus, I don’t like copying everything from my old laptop to my new one. There is a lot of stuff that I could throw away, but it takes time to go through it all. So I copied just my old home directory, but not the Applications directory. I know there’s lots of stuff that won’t run or that should be updated, so I installed all that stuff manually.

Anyway, here’s the info about my new laptop.

So it’s not a current laptop. I bought it off the clearance section because I wanted a laptop that still had usb connections. The idea of carrying a bunch of dongles to plug stuff into a usb-c connection fills me with dread. I’ll still have to do that for my network connection, but I do have usb and can still use my old chargers with an adapter. That’s good because I didn’t want to have to buy an extra one so that I could have one at home and work. I also bought a new 1tb hard drive and installed that myself. I couldn’t customize the laptop because I bought it in the clearance section, so it came with a 128mb drive, which is laughable. It also comes only with 8gb of ram and I can’t upgrade that. That’s sad because my old laptop had 16gb of ram and I wanted one with even more, but Apple has decided that we don’t need that much ram.

In my mind, I’m hoping this laptop takes me through retirement. And if that’s the case, this will perhaps be my last mac. I’m not liking the direction that their hardware is taking and if I don’t have to support macs at work, I really won’t need one. I’d be fine with a linux laptop.

Took me a while, but my point of this post is to note that I’m moving from a 15-inch laptop to a 13-inch laptop. Yes, this new laptop is much lighter than my old one. But the screen feels really small as well. One place where I really notice it is in my calendar. The cells are all much smaller so that I can’t see all my events. This may change how I use it. Now, I put lots of notes and reminders in my calendar. But if I can only view two or three events, I may stop putting items there. I’m already noticing that I don’t really pay attention to the line that says 2 more… or 3 more…, meaning if I don’t see them in the monthly view, it’s like they’re not there.

Another spot where I’ve noticed the smaller screen is in watching nhl games. It’s changed the arrangement of how things are laid out. I’ll probably get used to it, but my immediate reaction of course is that I don’t like it. Oh well, change is inevitable.

Learning More Windows 10 Stuff

My last post talked about using xcopy to copy a bunch of files from one server to another. I had been running this for a day, but a couple of times the command failed with the error “Insufficient Memory”. Since my new server has four times as much memory and three or four times as much disk space, I didn’t think that error was actually true. A quick google search shows that this message usually comes up when filenames are over 255 characters long, which was going to be a problem for me. Instead, robocopy (robust copy) should be used. The command I’m now trying is:

robocopy U:\ F:\data /MIR /Z /XA:H /W:10

/MIR = specifies that robocopy should mirror the source directory and the destination directory. Beware that this may delete files at the destination
/Z = ensures robocopy can resume the transfer of a large file in mid-file instead of restarting
/XA:H = makes robocopy ignore hidden files, which is usually system stuff that we don’t care about
/W:10 = reduces the wait time between failures to 10 seconds instead of the 30 second default

There’s also one very large directory with software that I’ve downloaded that I don’t want to copy. I can always just redownload it, so no sense in taking up space on my backup drive. So the command that I’m finally using is:

PS C:\> robocopy f:\data j:\edgwin\data /MIR /Z /XA:H /W:10 /XD "F:\data\installed_software"