Andrea Wayte

Programmer. Crafter. Clothing maker. Baker. Cyclist.

Upgrading to Webpacker 4 and Babel 7

23 May 2019

Upgrading to Webpacker 4 took a few extra steps for me. For those of you who also have to do some tweaking after using the babel-upgrade gem, this guide is for you!

Upgrade to Webpacker 4

bundle update webpacker
rails webpacker:binstubs
yarn upgrade @rails/webpacker --latest
yarn upgrade webpack-dev-server --latest

Upgrade babel

There is a gem that does all the changes for you, but I still had some changes to make after https://github.com/babel/babel-upgrade

    Update packages in package.json, the above repo outlines the changes well.
    Choose your configuration

In babel 7, the working directory is considered the root. For a project wide configuration, use a babel.config.is file in the root directory. If you have babel config for a package that is not the root, place a .babelrc file in the root of the package. You can have both types of config in a project. Config Files · Babel Babel has two parallel config file formats, which can be used together, or independently. Project-wide configuration… babeljs.io

Remove polyfills

First, what are polyfills? Polyfills enable us to write modern code, which will work in older browsers

The way babel works is that it takes the syntax, let’s say ES6, and transpiles it to syntax all browsers can understand. Such as let and const turning into var

A polyfill defines a new object or method in browsers that don’t support that object or method. There are a variety of polyfills, some examples include Map and Promise

Make sure to include @babel/polyfill as a dependency, not devDependency!

Update package renames

For all plugins, make sure to find out if it has been renamed! Yarn install and happy hacking!


DDD

23 Feb 2019

Often times software design is described as layers of a cake. These different layers are common in Object Oriented Programming, where not only is there MVC, but service objects, form objects and more. The cake needs to be sliced vertically as well. These vertical layers/sub systems are often called Bounded Contexts. The bounded context is a central pattern in Domain Driven Design. This is a solution for dealing with large models and teams. The idea is to have smaller models and clear data ownership. Think about which attributes change together in response to certain actions, and who changes those attributes. If something is bound within certain conditions, it might be its own context.

The pitfall developers fall into is adding one more attribute, one more column because we don’t see how it fits into the whole system at that time.

Application design should not be coupled directly to UI, though common in CRUD apps. It may be that the user might change different aspects on certain occasions, like a password, profile photo, address, etc. These CAN have their own submit buttons, or auto-save.

When deciding how to modularize a class, ask the following questions:

Why are they in the same db table and ActiveRecord model? Is it because they are updated on the same page?

Naming. For example, encrypted_password is not very similar to last_login_date, or avatar_image_url Coupling in updates; which attribute are often updated together business wise. Transitional consistency - which attributes need to be updated together in the same transaction. What happens if there is an error and only one command finishes, are there any consequences? Those classes with attributes that cooperate together and need to be updated together to abide by business rules are called Aggregates

When you extract attributes into smaller classes, you’ve created a bounded context!

Dependency directions

Sometimes there is a class that can read from other things, but the problem with this is that the class is reliant on the other classes, and limited in what in what it can do. For instance, it can not remove without deleting the other record.  Another solution is for data to be published from one bounded context and consumed by another bounded context, resulting in its own storage. Usually this is used when the context needs less data than what was published. Since the Active Record API is so wide, it is easy to expose data from other pats without an interface, such as using associations. This makes it difficult to know which parts of the app are reading the data. So how do you expose data from one part to another via an interface? Create an interface connecting the two parts (contexts).

Value Objects

describe, quantify, or measure an entity, such as Product or Order. Value objects have no identity, are are compared by the values they encapsulate, are are immutable. They are meant to pass information around, instead of passing solely attributes. 

Entities

a domain object with a unique identity, usually mutable, can have a lifecycle, and attributes/value objects describing it. 

Aggregates

a graph of domain objects you could load from the database, often seen as a cluster of objects. So if we change them, they should fully work or fail and rollback. Aggregates are used to protect business logic/requirements. The idea is to have small clusters of objects that do not easily cross each other.


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