Skip to content

Commit

Permalink
Hopefully successful merge
Browse files Browse the repository at this point in the history
  • Loading branch information
PragTob committed Dec 10, 2023
2 parents 330c403 + 392fe4d commit 7bf85bd
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 36 deletions.
1 change: 1 addition & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test,samples,mix}/**/*.{ex,exs}"]
]

19 changes: 14 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# The directory Mix will write compiled artifacts to.
/_build
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover
/cover/

# The directory Mix downloads your dependencies sources to.
/deps
/deps/

# Where 3rd-party dependencies like ExDoc output generated docs.
/doc
# Where third-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
deep_merge-*.tar

# Temporary files for e.g. tests.
/tmp/
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 1.0.0 (2019-03-26)

* Added the possibility to `@derive [DeepMerge.Resolver]` if you want your custom structs to also easily be deep_mergeable
Expand Down
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# DeepMerge [![Hex Version](https://img.shields.io/hexpm/v/deep_merge.svg)](https://hex.pm/packages/deep_merge) [![docs](https://img.shields.io/badge/docs-hexpm-blue.svg)](https://hexdocs.pm/deep_merge/) [![CI](https://github.com/PragTob/deep_merge/actions/workflows/main.yml/badge.svg)](https://github.com/PragTob/deep_merge/actions/workflows/main.yml) [![Coverage Status](https://coveralls.io/repos/github/PragTob/deep_merge/badge.svg?branch=master)](https://coveralls.io/github/PragTob/deep_merge?branch=master) [![Inline docs](http://inch-ci.org/github/PragTob/deep_merge.svg?branch=master)](http://inch-ci.org/github/PragTob/deep_merge)
# DeepMerge [![Hex Version](https://img.shields.io/hexpm/v/deep_merge.svg)](https://hex.pm/packages/deep_merge) [![docs](https://img.shields.io/badge/docs-hexpm-blue.svg)](https://hexdocs.pm/deep_merge/) [![CI](https://github.com/PragTob/deep_merge/actions/workflows/main.yml/badge.svg)](https://github.com/PragTob/deep_merge/actions/workflows/main.yml) [![Coverage Status](https://coveralls.io/repos/github/PragTob/deep_merge/badge.svg?branch=master)](https://coveralls.io/github/PragTob/deep_merge?branch=master) [![Total Download](https://img.shields.io/hexpm/dt/deep_merge.svg)](https://hex.pm/packages/deep_merge) [![License](https://img.shields.io/hexpm/l/deep_merge.svg)](https://github.com/PragTob/deep_merge/blob/main/LICENSE)

Provides functionality for "deep merging" maps and keyword lists in elixir, which is if during merging both values at the same key are maps/keyword lists merge them recursively. This is done via a protocol so can be extended for your own structs/data types if needbe.

Expand All @@ -24,13 +24,15 @@ I wanted this to be a feature of Elixir itself, however the proposal [was reject

## Installation

Add `deep_merge` to your list of dependencies in `mix.exs`:
Add `:deep_merge` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[{:deep_merge, "~> 1.0"}]
end
```
```elixir
def deps do
[
{:deep_merge, "~> 1.0"}
]
end
```

## General Usage - deep_merge/2

Expand All @@ -50,7 +52,7 @@ It is worth noting that structs are not deeply merged - not with each other and

What is merged and how is defined by implementing the `DeepMerge.Resolver` protocol. This library implements it for `Map`, `List` and falls back to `Any` (where the right hand side value/override is taken).

If you want your own struct to be deeply merged you can simply `@derive` the protocole:
If you want your own struct to be deeply merged you can simply `@derive` the protocol:

```elixir
defmodule Derived do
Expand Down Expand Up @@ -98,7 +100,7 @@ iex> DeepMerge.deep_merge(%{a: %{b: 1}, c: [d: 1]},
%{a: %{b: 1, z: 5}, c: [x: 0]}
```

This function is called for a given merge conflict with the key where it occured and the two conflicting values. Whatever value is returned in this function is inserted at that point in the structure - unless `DeepMerge.continue_deep_merge` is returned in which case the deep merge continues as normal.
This function is called for a given merge conflict with the key where it occurred and the two conflicting values. Whatever value is returned in this function is inserted at that point in the structure - unless `DeepMerge.continue_deep_merge` is returned in which case the deep merge continues as normal.

When would you want to use this versus a protocol? The best use case I can think of is when you want to alter behavior for which a protocol is already implemented or if you care about specific keys.

Expand All @@ -111,7 +113,7 @@ Well not necessarily, no. There are [very simple implementations for maps that u
There are subtle things that can be missed there though (and I missed the first time around):

* the most simple implementation also merges structs which is not always what you want
* For keyword lists on the other hand you gotta be careful that you don't accidentally merge keyword lists with lists as that's [currently possible](https://github.com/elixir-lang/elixir/issues/5395)
* for keyword lists on the other hand you gotta be careful that you don't accidentally merge keyword lists with lists as that's [currently possible](https://github.com/elixir-lang/elixir/issues/5395)
* you might want to further adopt the implementation, in [benchee](https://github.com/bencheeorg/benchee) we have 2 custom implementations of the protocol due to our needs

This library takes care of those problems and will take care of further problems/edge cases should they appear so you can focus on your business logic.
Expand All @@ -120,10 +122,17 @@ At the same time it offers extension mechanisms through protocols and a function

## Performance

You can check out [a benchmark and its results](https://github.com/PragTob/deep_merge/blob/master/benches/bench/deep_merge.exs).
You can check out [a benchmark and its results](https://github.com/PragTob/deep_merge/blob/main/benches/bench/deep_merge.exs).

The TLDR; is this: In the sample it is about 30 times slower than `Map.merge/2` - however, less than twice as slow as calling `Map.merge/3` with simple overriding behaviour (same behaviour as `Map.merge/2`). This is because `Map.merge/2` is highly optimized, but we need to do much more than the `Map.merge/3` sample in the benchmark so I think it's a very passable result. We're still talking about a couple of μs.

## Considered feature-complete

Unless you come with great feature ideas of course ;) So if you come here and there are no recent commits, don't worry - there are no known bugs or whatever. It's a small little library that does its job.

## Copyright and License

Copyright (c) 2016 Tobias Pfeiffer

This library is MIT licensed. See the
[LICENSE](https://github.com/PragTob/deep_merge/blob/main/LICENSE.txt) for details.
46 changes: 26 additions & 20 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule DeepMerge.Mixfile do
use Mix.Project

@source_url "https://github.com/PragTob/deep_merge"
@version "1.0.0"

def project do
Expand All @@ -12,58 +13,63 @@ defmodule DeepMerge.Mixfile do
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
deps: deps(),
docs: [source_ref: @version],
docs: docs(),
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
coveralls: :test,
"coveralls.detail": :test,
"coveralls.post": :test,
"coveralls.html": :test,
"coveralls.travis": :test
],
package: package(),
name: "deep_merge",
source_url: "https://github.com/PragTob/deep_merge",
description: """
Deep (recursive) merging for maps, keyword lists and whatever else
you may want via implementing a simple protocol.
""",
dialyzer: [
flags: [:error_handling, :underspecs],
ignore_warnings: ".dialyzer_ignore.exs",
list_unused_filters: true
],
description: """
Deep (recursive) merging for maps, keyword lists and whatever else
you may want via implementing a simple protocol.
"""
preferred_cli_env: [
docs: :docs,
coveralls: :test,
"coveralls.detail": :test,
"coveralls.post": :test,
"coveralls.html": :test,
"coveralls.travis": :test
]
]
end

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

# Configuration for the OTP application
#
# Type "mix help compile.app" for more information
def application do
[]
end

defp deps do
[
{:credo, "~> 1.0", only: :dev},
{:ex_doc, "~> 0.11", only: :dev},
{:earmark, "~> 1.2", only: :dev},
{:ex_doc, ">= 0.0.0", only: :docs, runtime: false},
{:excoveralls, "~> 0.7", only: :test},
{:inch_ex, "~> 2.0", only: :docs},
{:dialyxir, "~> 1.0", only: [:dev], runtime: false}
]
end

defp docs do
[
extras: ["CHANGELOG.md", "README.md"],
main: "readme",
source_url: @source_url,
source_ref: @version
]
end

defp package do
[
maintainers: ["Tobias Pfeiffer"],
licenses: ["MIT"],
links: %{
"github" => "https://github.com/PragTob/deep_merge"
"Changelog" => "https://hexdocs.pm/deep_merge/changelog.html",
"GitHub" => @source_url
}
]
end
Expand Down

0 comments on commit 7bf85bd

Please sign in to comment.