Andrea Wayte

Programmer. Crafter. Clothing maker. Baker. Cyclist.

General solutions to common problems

03 Feb 2019

Writing down what I learn is a general solution for my common problem: I forget lots of things. I found this in my notes, and it needed a home:

Design patterns are general solutions to common problems. A pattern in a guideline for flexible and resilient code design. These problems can be implemented in many different programming languages.

The Observer Pattern

An object maintains a list of dependents (observers) and notifies them automatically of any state changes by calling a method on the observers, often named update. 

Often used in event driven programming, the subject (which is being observed), and when its state changes this is communicated to the observers. To decouple the direct knowledge of each other, the pub-sub pattern often uses a message queue server which is accessed by the observers, so they subscribe to certain types of messages, only knowing about the message, not the source of the message, and vice versa. 

Inheritance

When you find yourself overwriting methods in the super class, you aren’t using inheritance well, code is duplicated across classes. Hard to gain knowledge about all ducks from the super class. Changing the super class affects other ducks. 

Interfaces

Allows different classes to share similarities (other specific classes), and not all classes need to have the same behavior. But destroys code reuse, since there are many interfaces to choose from. 

Principles

#1 Identify the aspects of your code that vary and separate them from what stays the same. ‘Encapsulate what varies’, pull them out at separate. By isolating what changes, this makes code more flexible for future changes.  #2 Program to an interface, not on implementation. ‘has-A’ relationship. The interfaces are a class that implements a specific behavior.  class Quack implements QuackBehavior.


Postgres, Rails, and ANY

03 Dec 2018

We needed to develop a new feature which allowed users to enable certain events to receive emails for. Before, each event sent emails to everyone in the account. However, some users did not need notifications for each event, so we needed to develop the ability to select which types of events to receive emails. We needed to develop a new feature which allowed users to enable certain events to receive emails for. Before, each event sent emails to everyone in the account. However, some users did not need notifications for each event, so we needed to develop the ability to select which types of events to receive emails.

I spent some time researching and brainstorming how to tackle this. I knew we wanted to store the option types in an array, so I was on the hunt for which data type to use. I have used jsonb before, but was feeling this might be too heavy for storing a simple array. Adding new columns for each event was something I wanted to avoid, in case we need to add more types of email events. I decided to settle on json (at first).

add_column :email_contacts, :event_types, :string, array: true, default: ['creation', 'sign', 'update']

The next challenge was to find the most efficient way to determine if a record had a certain item in the array. I came across ANY.

scope :creation_event, -> { where("'creation' = ANY(event_types)") }

This scope returns all the records which includes creation in the array of event_types. With the addition of :pluck, I was able to quick get an array of all the email addresses!


Rails Active Support Quick Guide

04 Sep 2018

In my effort to learn more about Rails and all of it’s magic, I dove into the docs and wrote up a concise version from Ruby Guides. This is a concise version from Ruby Guides. For more methods and in depth documentation, visit https://guides.rubyonrails.org/active_support_core_extensions.html

#blank?, #present?

blank values include nil, false, strings of whitespace, empty arrays/hashes

#presence

returns receiver or nil

name = user[:name].presence ‘No name’

#deep_dup

Dup is a Ruby method, but does not dup an object containing other objects.

#try

# without try
  unless @number.nil?
    @number.next
  end

# with try
  @number.try(:next)

#to_param

create a custom param for an object

class Article
  def to_param
    "#{id}=#{name.paramterize}"
  end
endarticle_path(@article)
=> "/articles/1-dog-show"

#to_query(namespace = nil)

constructs a query string from a hash. If given a namespace, it will be used to enclose key names and use any to_param.

{ name: 'bob', age: '25' }.to_query
=> "name=Bob&age=25"

#with_options

A way to group options, often used with ActiveRecord models.

#instance_values

returns a hash that maps instance values without the ‘@’

class User
  def initialize(name, age)
    @name, @age = name, age
  end
end

User.new("Andrea", 25).instance_values
=> {"name" => "Andrea", "age" => 25}

#instance_variable_names

returns an array of instance variables

class User
  def initialize(name, age)
    @name, @age = name, age
  end
end

User.new("Andrea", 25).instance_variable_names
=> [ "@name", "@age" ]

#in

returns whether an object is in another object, returns a boolean or an error if the argument does not respond to include?

5.in?([1,2,3,4,5])    # => true
"mo".in?("tomorrow")  # => true
25.in?(50..100)       # => false
1.in?(1)              # => ArgumentError

delegate, delegate_missing_to

forwards methods. If there is an association between 2 objects, this way they can forward methods instead of object.method_name

class Article < ApplicationRecord
  has_one :author  delegate :author_name, to: author
  delegate_missing_to :author
end

#html_safe

Marks strings as being safe to insert into HTML

"".html_safe? => false
s = "".html_safe
s.html_safe? # => true

#squish

Strips leading and trailing whitespace, and substitutes with a space.

" \n  disco\n\r \t ball \n".squish # => "disco ball"

#truncate, #truncate_words

returns a copy of its receiver based on given length

"A long time ago, in a galaxy far, far, away".truncate(18)
=> "A long time ago...""A long time ago, in a galaxy far, far, away".truncate_words(4)
=> "A long time ago..."

#starts_with?, #ends_with?

"hello".starts_with?("h") => true
"hello".ends_with?("h") => false

#pluralize(count = 0), #singularize

"animal".pluralize => "animals"
"animals".singularize => "animal

#camelize, #underscore

"full_name".camelize => "FullName"
"full_name".camelize(:lower) => "fullName""fullName".underscore => "full_name"
"User::Session".underscore => "user/session"

#humanize

"name".humanize                         => "Name"
"author_id".humanize                    => "Author"
"author_id".humanize(capitalize: false) => "author"
"comments_count".humanize               => "Comments count"
"_id".humanize                          => "Id"

#symbolize_keys

{ nil => nil, 5 => 5, "name" => "Bob }.symbolize_keys
=> { nil => nil, 5 => 5, :name => "Bob }.

For more methods, visit https://guides.rubyonrails.org/active_support_core_extensions.html


Rails Active Record

31 Aug 2018

After several months of working with Ruby on Rails, I want to gain a deeper understanding of Active Record in efforts to optimize performance and understand more of the good ol’ Rails magic. I drew information from many resources, which I will include as links at the bottom. This is a general overview of common concepts of Active Record.

**Active Record Classes**

ActiveRecord::FinderRelations
Returns model instance, and run queries immediatelyraises error if not found
#find
returns nil if not found
#find_by
#first
#last
#exists?

**ActiveRecord::QueryMethods**
Returns relation, only runs query when calledreturns an array
#where
#group
#distinct
#includes
#joins
#select

Aggregations

Useful when performing mathematical operations, such as #count or #max.

Post.joins(:tags).group("tags.name").count
  # => {"tag1" => 4, "tag2" => 2, "tag3" => 5}

Scopes

Chains of ActiveRecord methods that are used with a relation. Scopes return relations, so you can chain them.

class User < ActiveRecord::Base
  scope :millennials, -> { where('age < 35') }
end

Retrieve large batches

Works for models and relations, and can chain methods

#find_eachUser.find_each(batch_size: 5000) do |user|
  NewsMailer.weekly(user).deliver_now
end
options: batch_size, start, finish, error_on_ignore#find_in_batches

Conditions can be used with ActiveRelations

Client.where("orders_count = ?", params[:orders])

Joins (lazy loading)

Lazy loading: queries are not fired until called upon

> user = User.where('age > 18')
> user.all #queries fired

# Returns all Users that have a blog post

users_with_posts = User.joins(:post)
users_with_posts.all

# Returns all User objects with posts in which have comments

User.joins(:post, :comment)

# join nested associations

User.joins(post: :something)

Includes (eager loading, minimal queries possible)

ActiveRecored lets you specify 1+ associations beforehand. It eagerly loads the table so there are not extra queries

User.includes(:posts, :followers)

N + 1 problem

We want to avoid unnecessary queries through associations.

#includes uses eager loading

An example of eager loading is #pluck. It pulls up a bunch of records and stores in memory, grabs the column and returns an array.

Joins vs Includes

Use joins when you don’t want to access data from the associated table

Use includes when you want to access data from the associated table

More Resources api.rubyonrails.org guides.rubyonrails.org (rubyinrails.com)[rubyinrails.com]


Another way to implement React with Ruby on Rails on Heroku

23 Apr 2018

A tutorial to set up a rails app on Heroku!

React is gaining popularity for good reason. It creates a virtual DOM and uses renderable components which results in a speedy application. For my application, I decided to use React for my client side and Ruby on Rails as a JSON API. I had spent much time searching for the best solution only to find… there are many solutions. After discussing with my partner, I decided to incorporate React as a bundle file that Rails will serve.

Rails will serve any file that is located in the public folder, so I used this to my advantage and set up wepback to bundle the Javascript file to the public folder.

My file structure looks like this:

-app
   //Rails files in here, such as controllers, models, etc
...
-client
   //React files in here, such as dist, src, etc
...
-public
   -app
      -assets
      -bundle.js
      -index.html
...
-package.json //include Webpack config in here!

I include the package.json file for the client in the root folder so Heroku will follow the instructions to bundle the files when deploying to production. You also need to tell webpack where to place the bundled Javascript file.

Change this in your webpack.config.js…

...
output: {
   filename: 'bundle.js',
   path: resolve(__dirname, '../public/app/'),
   publicPath: '/app/'
},
...

Now Webpack knows where to place the file. Next we want to configure Node to bundle after dependencies are installed. We do this with heroku-postbuild.

"scripts": {
   "start": "react-scripts start",
   "bundle": "node_modules/.bin/webpack --config   ./client/webpack.deploy.config.js --progress --colors",
   "test": "react-scripts test --env=jsdom",
   "build-css": "node-sass-chokidar --include-path ./client/src --include-path ./node_modules src/ -o src/",
   "watch-css": "npm run build-css && node-sass-chokidar --include-path ./client/src --include-path ./node_modules src/ -o src/ --watch --recursive",
   "build": "react-scripts build",
   "watch": "webpack --config ./client/webpack.config.js --watch",
   "heroku-postbuild": "npm run bundle"
}

Make sure to set up a Heroku buildpack. You can do this in the Heroku CLI.

heroku buildpacks:clear
heroku buildpacks:set heroku/nodejs
heroku buildpacks:add heroku/ruby --index 2

Now you are all set to develop a React/Rails project! All you got to do is npm run watch and rails server and start building!