Skip to content

Commit

Permalink
refactor: restructure repo layout (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
kimburgess authored Jun 14, 2021
1 parent fa87521 commit 3f4f379
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 178 deletions.
69 changes: 38 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,30 @@ A collection of classes, modules, macros and standard library extensions to simp

Each tool is small, independent and generic.
To use a tool, explicitly `require` where needed.

```crystal
require "inactive_support/<tool name>"
```

Their usage should be highly intentional and not a default choice.


## Tools

### `args`

Enables `args` to be stated anywhere within a method body.
This is substituted with a NamedTuple containing the arguments of the surrounding method.

```crystal
def example(a : String, b : String, c : String)
args
end
example "foo", "bar", "baz" # => {a: "foo", b: "bar", c: "baz"}
```


### `collection`

The `Collection` module provides an interface and tools for working with any collection type.
Expand Down Expand Up @@ -40,37 +59,7 @@ nested.traverse.to_h # =>
```


### `core_ext/presence`

Provides `Object#presence`.

This Allows one to check for the presence of useful data in container types such as `Array` and `Hash`.

```crystal
# hash loaded from config file or database
# imagine this is `Hash(String, String) | Nil`
my_hash = {} of String => String
# set some defaults
my_hash = my_hash.presence || {"default" => "settings"}
```


### `macro/args`

Enables `args` to be stated anywhere within a method body.
This is substituted with a NamedTuple containing the arguments of the surrounding method.

```crystal
def example(a : String, b : String, c : String)
args
end
example "foo", "bar", "baz" # => {a: "foo", b: "bar", c: "baz"}
```


### `macro/mapped_enum`
### `mapped_enum`

Provides support for defining non-integer enum types.
```crystal
Expand Down Expand Up @@ -104,3 +93,21 @@ Example::A.mapped_value # => "foo"
```

All other functionality and safety that enums provide holds.


### `presence`

Provides `Object#presence`.

This Allows one to check for the presence of useful data in container types such as `Array` and `Hash`.

```crystal
# hash loaded from config file or database
# imagine this is `Hash(String, String) | Nil`
my_hash = {} of String => String
# set some defaults
my_hash = my_hash.presence || {"default" => "settings"}
```


2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: inactive-support
crystal: ">= 0.36.0"
version: 0.3.0
version: 0.4.0
license: MIT

authors:
Expand Down
2 changes: 1 addition & 1 deletion spec/macro/args_spec.cr → spec/args/args_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require "spec"
require "../../src/macro/args"
require "../../src/args"

def args_test(a, b, c)
args
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require "spec"
require "../src/collection"
require "../../src/collection"

describe Collection do
describe "#[]?" do
Expand Down
Empty file removed spec/core_ext/.keep
Empty file.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require "spec"
require "../../src/macro/mapped_enum"
require "../../src/mapped_enum"

mapped_enum SpecMappedEnum do
A = "foo"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require "spec"
require "../../src/core_ext/presence"
require "../../src/presence"

describe "core_ext/presence" do
it "checks for presence of data in objects" do
Expand Down
File renamed without changes.
144 changes: 2 additions & 142 deletions src/collection.cr → src/collection/collection.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
require "json"
require "yaml"
require "http"
require "uri"
require "./diggable"

# The `Collection` module provides an interface and tools for working with any
# collection type. This includes both index and key-based containers.
Expand Down Expand Up @@ -174,141 +171,4 @@ module Collection(X, Y)
end
end

module Digable(X)
macro included
{% methods = @type.methods.map(&.name.stringify) %}
{% if !methods.includes? "dig" %}
def dig(key_or_index : X, *subkeys)
if (value = self[key_or_index]) && value.responds_to?(:dig)
return value.dig(*subkeys)
end
if key_or_index.is_a? Int
raise IndexError.new "#{self.class} value not diggable for index: #{key_or_index.inspect}"
else
raise KeyError.new "#{self.class} value not diggable for key: #{key_or_index.inspect}"
end
end

# :nodoc:
def dig(key_or_index : X)
self[key_on_index]
end

def dig?(key_or_index : X, *subkeys)
if (value = self[key_or_index]?) && value.responds_to?(:dig?)
value.dig?(*subkeys)
end
end

# :nodoc:
def dig?(key_or_index : X)
self[key_or_index]?
end
{% end %}
end
end

module Indexable(T)
include Collection(Int32, T)

def each_pair(&block) : Nil
each_with_index { |e, i| yield({i, e}) }
end

def each_pair : Iterator({Int32, T})
each.with_index.map { |(e, i)| {i, e} }
end
end

struct NamedTuple(T)
# FIXME: the co-domain should be a `Union(T.values)`, however this is not
# expressable with the current compiler.
# See: https://github.com/crystal-lang/crystal/issues/6757
include Collection(Symbol, NoReturn)

def each_pair(&block) : Nil
each { |k, v| yield({k, v}) }
end

def each_pair : Iterator
to_a.each
end
end

class Hash(K, V)
include Collection(K, V)

def each_pair(&block) : Nil
each { |k, v| yield({k, v}) }
end

def each_pair : Iterator({K, V})
each
end
end

struct JSON::Any
include Collection(String | Int32, JSON::Any)

def each_pair(&block) : Nil
if value = as_h? || as_a?
value.each_pair { |k, v| yield({k, v}) }
end
end

protected def nested? : Bool
!(as_h? || as_a?).nil?
end
end

struct YAML::Any
include Collection(String | Int32, YAML::Any)

def each_pair(&block) : Nil
if value = as_a?
value.each_pair { |k, v| yield({k, v}) }
elsif value = as_h?
value.each_pair { |k, v| yield({k.to_s, v}) }
end
end

protected def nested? : Bool
!(as_h? || as_a?).nil?
end
end

class HTTP::Cookies
include Collection(String, HTTP::Cookie)

def each_pair(&block) : Nil
each { |c| yield({c.name, c}) }
end

def each_pair
each.map { |c| ({c.name, c}) }
end
end

struct HTTP::Headers
include Collection(String, Array(String))

def each_pair(&block) : Nil
each { |k, v| yield({k, v}) }
end

def each_pair : Iterator({String, Array(String)})
each
end
end

struct URI::Params
include Collection(String, String)

def each_pair(&block) : Nil
each { |k, v| yield({k, v}) }
end

def each_pair : Iterator({String, String})
each
end
end
require "./core_ext"
110 changes: 110 additions & 0 deletions src/collection/core_ext.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
require "json"
require "yaml"
require "http"
require "uri"
require "./collection"

module Indexable(T)
include Collection(Int32, T)

def each_pair(&block) : Nil
each_with_index { |e, i| yield({i, e}) }
end

def each_pair : Iterator({Int32, T})
each.with_index.map { |(e, i)| {i, e} }
end
end

struct NamedTuple(T)
# FIXME: the co-domain should be a `Union(T.values)`, however this is not
# expressable with the current compiler.
# See: https://github.com/crystal-lang/crystal/issues/6757
include Collection(Symbol, NoReturn)

def each_pair(&block) : Nil
each { |k, v| yield({k, v}) }
end

def each_pair : Iterator
to_a.each
end
end

class Hash(K, V)
include Collection(K, V)

def each_pair(&block) : Nil
each { |k, v| yield({k, v}) }
end

def each_pair : Iterator({K, V})
each
end
end

struct JSON::Any
include Collection(String | Int32, JSON::Any)

def each_pair(&block) : Nil
if value = as_h? || as_a?
value.each_pair { |k, v| yield({k, v}) }
end
end

protected def nested? : Bool
!(as_h? || as_a?).nil?
end
end

struct YAML::Any
include Collection(String | Int32, YAML::Any)

def each_pair(&block) : Nil
if value = as_a?
value.each_pair { |k, v| yield({k, v}) }
elsif value = as_h?
value.each_pair { |k, v| yield({k.to_s, v}) }
end
end

protected def nested? : Bool
!(as_h? || as_a?).nil?
end
end

class HTTP::Cookies
include Collection(String, HTTP::Cookie)

def each_pair(&block) : Nil
each { |c| yield({c.name, c}) }
end

def each_pair
each.map { |c| ({c.name, c}) }
end
end

struct HTTP::Headers
include Collection(String, Array(String))

def each_pair(&block) : Nil
each { |k, v| yield({k, v}) }
end

def each_pair : Iterator({String, Array(String)})
each
end
end

struct URI::Params
include Collection(String, String)

def each_pair(&block) : Nil
each { |k, v| yield({k, v}) }
end

def each_pair : Iterator({String, String})
each
end
end
Loading

0 comments on commit 3f4f379

Please sign in to comment.