Skip to content

Instantly share code, notes, and snippets.

@jayzz55
jayzz55 / PubKeys_add.ex
Created November 19, 2015 04:43
PubKeys_add
def add_user_key(ssh_key, files_list, path) do
files_list
|> Enum.reject(&empty?(&1))
|> Enum.map(&read_keys(&1, path))
|> Enum.reject(&should_skip?(&1, ssh_key))
|> Enum.map(&prepend_key(&1, ssh_key, path))
|> Enum.map(&scp_to_server(&1, path))
end
@jayzz55
jayzz55 / PubKeys_remove.ex
Created November 19, 2015 04:44
PubKeys_remove
def remove_user_key(ssh_key, files_list, path) do
files_list
|> Enum.reject(&empty?(&1))
|> Enum.map(&read_keys(&1, path))
|> Enum.reject(&should_skip?(&1, ssh_key, :remove))
|> Enum.map(&remove_key(&1, ssh_key, path))
|> Enum.map(&scp_to_server(&1, path))
end
@jayzz55
jayzz55 / Building SSH Key management tool with Elixir.md
Last active August 18, 2023 12:47
Building SSH Key management tool with Elixir

Building an SSH key management tool with Elixir

Sipping the Elixir

After being inspired by David's talk on Elixir at Hardhat, we decided to try and build our own SSH public key management tool, PubKeys, in Elixir. We're delighted to report back that Elixir is amazing.

Things we love about Elixir:

  • Writing code in a functional style encourages us to break the code into small chunks of functions.
  • Through the use of the function composition operator |>, the code is readable and elegant.
  • Pattern matching is great.

2: Designing classes with a single responsibility

Depend on Behaviour not Data

Hide Data Structures

Bad Examples:

class ObscuringReferences attr_reader :data
 def initialize(data)

3: Managing Dependencies

Dependency management is core to creating future-proof applications. Injecting dependencies creates loosely coupled objects that can be reused in novel ways. Isolating dependencies allows objects to quickly adapt to unexpected changes. Depending on abstractions decreases the likelihood of facing these changes. The key to managing dependencies is to control their direction. The road to maintenance nirvana is paved with classes that depend on things that change less often than they do.

Recognizing Dependencies

An object has a dependency when it knows:

  • The name of another class. Gear expects a class named Wheel to exist.

4: Creating Flexible Interfaces

Defining interfaces

Imagine an Object's interface is like a kitchen's interface. If a customer at a restaurant would like to order food, he will order the food and communicate with the kitchen through "menu". Menu the kitchen's interface.

The customer knows what it wants, and trust the kitchen to deliver what it wants. The customer does not need to know How the food will be prepared, nor does the customer tell the kitchen how to prepare it.

The kitchen only exposes it's public interface, Menu, and hides it's internal private interface such that customer can not go inside the kitchen and stir the pot.

5: Reducing Costs with Duck Typing

Duck types = public interfaces not tied to any specific class

It's not what an object is that matters, it's what it does.

Concrete code is easy to understand, but costly to extend. Abstract code may initially seem more obscure but, once understood, is far easier to change.

Once you begin to treat your objects as if they are defined by their behavior rather than by their class, you enter a new realm of expressive design.

6: Acquiring Behavior Through Inheritance

Inheritance is, at it's core, a mechanism for automatic message delegation. It defines a forward path for not-understood messages.

Subclasses are specializations of their superclasses

Template Method Pattern

Any class that implements the template method pattern must supply an implementation for every message it sends, even if all it does is raise a NotImplementedError.

7: Sharing Role Behavior With Modules

Combining the qualities of two existing subclasses is something Ruby cannot do (multiple inheritance)

Because no design technique is free, creating the most cost-effective application requires making informed tradeoffs between the relative cost and likely benefits of alternatives

Writing Inheritable Code

  • Recognize the antipatterns
  • Insist on the abstraction

8: Combining Objects with Composition

Composition describes a has a relationship.

  • Composition: objects "inside" have no meaning outside that context

  • Aggregation: like composition except objects "inside" have meaning outside that context

Deciding Between Inheritance and Composition