Skip to content

Commit

Permalink
Adds lib/ folder ignored for some reason
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn committed Jun 17, 2017
1 parent 1b525de commit 64c797a
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ erl_crash.dump
*.beam

#### custom ####
doc/
/doc
7 changes: 7 additions & 0 deletions lib/petrovich_elixir.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule PetrovichElixir do
@moduledoc """
Documentation for Petrovich.
Elixir library to inflect Russian first, last, and middle names.
"""
end
46 changes: 46 additions & 0 deletions lib/petrovich_elixir/applier.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
defmodule PetrovichElixir.Applier do
@moduledoc """
Applies rules on value.
This module applies inflection rules on raw values.
It is tightly coupled with `Parser`, which supplies the rules.
"""

@rules %{
genitive: 0,
dative: 1,
accusative: 2,
instrumental: 3,
prepositional: 4,
}

def apply(data, case_, %{"mods" => mods}) do
mod = Enum.at(mods, @rules[case_])
apply_mod(mod, data)
end

defp apply_mod(".", data), do: data
defp apply_mod(mod, data) do
case count_replaces(mod) do
0 -> data <> mod
_ -> replace(mod, data)
end
end

defp replace(mod, data) do
replaces = count_replaces(mod)
length = String.length(data)

root = String.slice(data, 0, length - replaces)
root <> String.replace(mod, "-", "")
end

defp count_replaces(mod) do
mod
|> String.graphemes
|> Enum.reduce(%{}, fn(letter, acc) ->
Map.update(acc, letter, 1, &(&1 + 1))
end)
|> Map.get("-", 0)
end
end
19 changes: 19 additions & 0 deletions lib/petrovich_elixir/config.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule PetrovichElixir.Config do
@moduledoc """
Provides application/system environment variable lookup at runtime
"""

@doc """
Returns the requested variable
"""

@spec get_env(atom, atom, atom | map) :: term
def get_env(application, key, default \\ nil) do
application
|> Application.get_env(key, default)
|> _get_env()
end

defp _get_env({:system, env_variable}), do: System.get_env(env_variable)
defp _get_env(value), do: value
end
13 changes: 13 additions & 0 deletions lib/petrovich_elixir/detector.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule PetrovichElixir.Detector do
@moduledoc """
Guesses the gender by person's name.
"""

@doc """
Returns gender from person's name
"""
def detect_gender(values, type) do

end

end
76 changes: 76 additions & 0 deletions lib/petrovich_elixir/parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
defmodule PetrovichElixir.Parser do
@moduledoc """
Parser receives the name and case to infect it to.
Then it parses the rules to find the appropriate modifications.
It then calls `Applier` to modify the value.
"""

alias PetrovichElixir.{NameStore, Applier}

def parse(data, _, :nomenative, _), do: data
def parse(data, type, case_, gender) do
apply_rule(data, to_string(type), case_, to_string(gender))
end

defp apply_rule(values, type, case_, gender) do
NameStore.start_link()

values
|> String.split("-")
|> Enum.map(fn(item) -> prepare_value(item, type, case_, gender) end)
|> Enum.join("-")
end

defp prepare_value(value, type, case_, gender) do
%{"exceptions" => exceptions,
"suffixes" => suffixes} = NameStore.get(type)

value
|> String.downcase
|> maybe_exception(gender, exceptions)
|> maybe_rule(gender, suffixes)
|> case do
{:error, _} -> value
{:ok, res} -> Applier.apply(value, case_, res)
end
end

defp maybe_exception(name, gender, exceptions) do
exceptions
|> Enum.filter(fn(item) -> fits?(:e, name, gender, item) end)
|> pick_one(name)
end

defp maybe_rule({:ok, rule}, _, _), do: {:ok, rule}
defp maybe_rule({:error, name}, gender, suffixes) do
suffixes
|> Enum.filter(fn(item) -> fits?(:r, name, gender, item) end)
|> pick_one(name)
end

defp fits?(:e, name, gender, rule) do
gender?(gender, rule) and name in rule["test"]
end
defp fits?(:r, name, gender, rule) do
name_len = String.length(name)

gender?(gender, rule) and Enum.any?(rule["test"], fn(test) ->
test_len = String.length(test)
test == String.slice(name, name_len - test_len, test_len)
end)
end

defp gender?(gender, rule) do
gender == rule["gender"] or rule["gender"] == "androgynous"
end

defp pick_one(items, name) do
items
|> Enum.at(0)
|> case do
nil -> {:error, name}
rule -> {:ok, rule}
end
end
end
24 changes: 24 additions & 0 deletions lib/petrovich_elixir/petrovich.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule PetrovichElixir.Petrovich do
@moduledoc """
Public interface to all the functions.
It can inflect first, middle, and last names.
It also can detect gender by name.
"""

alias PetrovichElixir.Parser

@default_gender :androgynous

def firstname(name, case_, gender \\ @default_gender) do
Parser.parse(name, :firstname, case_, gender)
end

def middlename(name, case_, gender \\ @default_gender) do
Parser.parse(name, :middlename, case_, gender)
end

def lastname(name, case_, gender \\ @default_gender) do
Parser.parse(name, :lastname, case_, gender)
end
end
47 changes: 47 additions & 0 deletions lib/petrovich_elixir/stores/base.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
defmodule PetrovichElixir.Store do
@moduledoc """
This module implements basic functionality for different agents.
This module provides a simple macro, when it is used
it inserts all the required `Agent`'s methods inside the
caller module.
"""

alias PetrovichElixir.Config

defmacro __using__(options) do
caller = __CALLER__.module

quote location: :keep, bind_quoted: [options: options, caller: caller] do
@setting Keyword.get(options, :setting)

def start_link do
Agent.start_link(fn ->
load_values() end, name: unquote(caller))
end

def stop do
Agent.stop(unquote(caller))
end

def get(key) do
Agent.get(unquote(caller), fn(state) ->
Map.get(state, key)
end)
end

def all do
Agent.get(unquote(caller), fn(state) ->
state
end)
end

defp load_values do
:petrovich_elixir
|> Config.get_env(@setting)
|> File.read!
|> Poison.decode!
end
end
end
end
20 changes: 20 additions & 0 deletions lib/petrovich_elixir/stores/gender_store.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule PetrovichElixir.GenderStore do
@moduledoc """
This module stores different genders.
It is then used to guess the gender by name,
last name or middle name.
"""

use PetrovichElixir.Store, setting: :gender_path

@name __MODULE__

def get(key1, key2) do
Agent.get(@name, fn(state) ->
state
|> Map.get(key1)
|> Map.get(key2)
end)
end
end
9 changes: 9 additions & 0 deletions lib/petrovich_elixir/stores/name_store.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule PetrovichElixir.NameStore do
@moduledoc """
This module stores different rules on how to inflect values.
It stores rules for first, middle and last names.
"""

use PetrovichElixir.Store, setting: :rules_path
end

0 comments on commit 64c797a

Please sign in to comment.