├── .gitignore ├── LICENSE.txt ├── README.md ├── config └── config.exs.sample ├── lib ├── sips_downloader.ex └── sips_downloader │ ├── dpdcart.ex │ ├── episode_downloader.ex │ ├── episodes_feed.ex │ ├── parallel_downloader.ex │ └── xml_parser.ex ├── misc ├── feed └── feed_without_description ├── mix.exs ├── mix.lock └── test ├── feed ├── full_feed ├── sips_downloader_test.exs └── test_helper.exs /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /deps 3 | erl_crash.dump 4 | *.ez 5 | deps 6 | config/config.exs 7 | sips_downloader 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Dávid Kovács 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SipsDownloader 2 | ============== 3 | 4 | Elixir module for downloading the ElixirSips episodes and all other files. 5 | 6 | The login credentials and download location must be set in config/config.exs. 7 | 8 | ## Installation 9 | 10 | Clone this repository 11 | 12 | ``` 13 | git clone https://github.com/DavsX/SipsDownloader 14 | cd SipsDownloader 15 | ``` 16 | 17 | Update config/config.exs, then generate an executable 18 | 19 | ``` 20 | mv config/config.exs.sample config/config.exs 21 | vim config/config.exs 22 | mix do deps.get, escript.build 23 | ``` 24 | 25 | Run the executable 26 | 27 | ``` 28 | ./sips_downloader 29 | ``` 30 | -------------------------------------------------------------------------------- /config/config.exs.sample: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :sips, 4 | download_dir: "/tmp", 5 | username: "username", 6 | password: "password" 7 | -------------------------------------------------------------------------------- /lib/sips_downloader.ex: -------------------------------------------------------------------------------- 1 | defmodule SipsDownloader do 2 | @moduledoc """ 3 | Downloads new ElixirSips episodes (subscription to ElixirSips required) 4 | 5 | Credentials and other information needs to be specified in config/config.exs (see provided sample) 6 | 7 | Execute: 8 | iex> SipsDownloader.run() 9 | """ 10 | 11 | def main(_args) do 12 | run 13 | end 14 | 15 | def run do 16 | username = Application.get_env(:sips, :username) 17 | password = Application.get_env(:sips, :password) 18 | download_dir = Application.get_env(:sips, :download_dir) 19 | 20 | session_id = 21 | SipsDownloader.DPDCart.get_login_url 22 | |> SipsDownloader.DPDCart.get_login_session(username, password) 23 | 24 | episodes_to_download = 25 | SipsDownloader.EpisodesFeed.download_episodes_feed(username, password) 26 | |> SipsDownloader.XMLParser.parse_episodes 27 | |> Enum.filter(&episode_to_download(download_dir, &1)) 28 | |> Enum.reverse 29 | 30 | if Enum.empty?(episodes_to_download) do 31 | IO.puts "No new episodes to download" 32 | else 33 | IO.puts "Downloading episodes" 34 | SipsDownloader.ParallelDownloader.run(episodes_to_download, session_id, download_dir) 35 | end 36 | end 37 | 38 | def episode_to_download(dir, {name, _}) do 39 | (Path.join(dir, name) |> File.exists?) == false 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/sips_downloader/dpdcart.ex: -------------------------------------------------------------------------------- 1 | defmodule SipsDownloader.DPDCart do 2 | def get_login_url do 3 | url = get_login_page() |> SipsDownloader.XMLParser.parse_login_url 4 | 5 | "https://elixirsips.dpdcart.com" <> url 6 | end 7 | 8 | def get_login_page do 9 | login_page_url = "https://elixirsips.dpdcart.com/subscriber/content" 10 | %HTTPoison.Response{body: body, status_code: 200} = HTTPoison.get!(login_page_url) 11 | body 12 | end 13 | 14 | def get_login_session(url, username, password) do 15 | headers = %{"Content-type" => "application/x-www-form-urlencoded"} 16 | post_data = {:form, [username: username, password: password]} 17 | {:ok, response} = HTTPoison.post(url, post_data, headers) 18 | extract_session_id(response.headers) 19 | end 20 | 21 | def extract_session_id(headers) do 22 | cookie = headers |> Enum.find(&cookie_header?/1) 23 | {_, "symfony=" <> cookie_val} = cookie 24 | cookie_val |> String.split(";") |> List.first 25 | end 26 | 27 | def cookie_header?(header) do 28 | case header do 29 | {"Set-Cookie", "symfony=" <> _} -> true 30 | _ -> false 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/sips_downloader/episode_downloader.ex: -------------------------------------------------------------------------------- 1 | defmodule SipsDownloader.EpisodeDownloader do 2 | def run(work = {_name, url}, state) do 3 | result = case make_async_request(url, state.session_id) do 4 | {:ok, %HTTPoison.AsyncResponse{}} -> 5 | state = Map.merge(state, %{fh: nil, file_size: nil, downloaded_size: nil}) 6 | process_download(work, state) 7 | 8 | {:error, %HTTPoison.Error{reason: reason}} -> 9 | {:error, reason} 10 | end 11 | 12 | send(state.parent, {state.work_ref, self(), work, result}) 13 | end 14 | 15 | defp make_async_request(url, session_id) do 16 | HTTPoison.get(url, [], [ 17 | hackney: [cookie: [{"symfony", session_id}]], 18 | stream_to: self() 19 | ]) 20 | end 21 | 22 | defp process_download(work = {name, _url}, state = %{fh: nil, dir: dir}) do 23 | receive do 24 | %HTTPoison.AsyncStatus{code: 200} -> 25 | {:ok, fh} = Path.expand(dir) |> Path.join(name) |> Kernel.<>(".tmp") |> File.open([:write, :append]) 26 | process_download(work, %{state | fh: fh}) 27 | 28 | %HTTPoison.AsyncStatus{code: 302} -> 29 | receive_redirect_location() 30 | 31 | %HTTPoison.AsyncStatus{code: code} -> 32 | {:error, "Could not download episode [#{name}]. Status code #{code}"} 33 | 34 | result -> 35 | {:error, "Expected %HTTPoison.AsyncStatus, got [#{inspect result}]"} 36 | after 37 | 5_000 -> {:error, "Expected %HTTPoison.AsyncStatus, got nothing after 5s"} 38 | end 39 | end 40 | 41 | defp process_download(work, state = %{file_size: nil, downloaded_size: nil}) do 42 | receive do 43 | %HTTPoison.AsyncHeaders{headers: headers} -> 44 | content_length = Enum.find(headers, fn {type, _} -> type == "Content-Length" end) 45 | |> elem(1) 46 | |> String.to_integer 47 | 48 | state = %{state | file_size: content_length, downloaded_size: 0} 49 | process_download(work, state) 50 | 51 | result -> 52 | {:error, "Expected %HTTPoison.AsyncHeaders, got [#{inspect result}]"} 53 | after 54 | 5_000 -> {:error, "Expected %HTTPoison.AsyncHeaders, got nothing after 5s"} 55 | end 56 | end 57 | 58 | defp process_download(work = {name, _url}, state) do 59 | file_size = state.file_size 60 | downloaded_size = state.downloaded_size 61 | fh = state.fh 62 | 63 | receive do 64 | %HTTPoison.AsyncChunk{chunk: chunk} -> 65 | chunk_len = byte_size(chunk) 66 | :ok = IO.binwrite(fh, chunk) 67 | process_download(work, %{state | downloaded_size: downloaded_size + chunk_len}) 68 | 69 | %HTTPoison.AsyncEnd{} -> 70 | file_name = Path.join(state.dir, name) 71 | temp_file_name = file_name <> ".tmp" 72 | 73 | if file_size == downloaded_size do 74 | :ok = File.cp(temp_file_name, file_name) 75 | :ok = File.close(fh) 76 | :ok = File.rm(temp_file_name) 77 | 78 | {:ok} 79 | else 80 | :ok = File.close(fh) 81 | :ok = File.rm(temp_file_name) 82 | 83 | {:error, "Did not manage to download the whole file! #{downloaded_size}/#{file_size}"} 84 | end 85 | 86 | result -> 87 | {:error, "Expected %HTTPoison.AsyncChunk/End, got [#{inspect result}]"} 88 | 89 | after 90 | 5_000 -> {:error, "Expected %HTTPoison.AsyncChunk/End, got nothing after 5s"} 91 | end 92 | end 93 | 94 | defp receive_redirect_location() do 95 | receive do 96 | %HTTPoison.AsyncHeaders{headers: headers} -> 97 | {"Location", location} = Enum.find(headers, fn {type, _} -> type == "Location" end) 98 | {:redirect, location} 99 | 100 | result -> 101 | {:error, "Expected %HTTPoison.AsyncHeaders, got [#{inspect result}]"} 102 | after 103 | 5_000 -> {:error, "Expected %HTTPoison.AsyncHeaders, got nothing after 5s"} 104 | end 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/sips_downloader/episodes_feed.ex: -------------------------------------------------------------------------------- 1 | defmodule SipsDownloader.EpisodesFeed do 2 | def download_episodes_feed(username, password) do 3 | feed_url = "https://elixirsips.dpdcart.com/feed" 4 | hackney = [basic_auth: {username, password}] 5 | 6 | IO.puts "Downloading episodes_feed" 7 | 8 | case HTTPoison.get(feed_url, [], [hackney: hackney]) do 9 | {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> 10 | body 11 | {:ok, %HTTPoison.Response{status_code: code}} -> 12 | raise "Failed to download feed. Status code #{code}" 13 | {:error, response} -> 14 | raise "Failed to download feed: #{response.reason}" 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/sips_downloader/parallel_downloader.ex: -------------------------------------------------------------------------------- 1 | defmodule SipsDownloader.ParallelDownloader do 2 | def run(episodes, session_id, download_dir) do 3 | {:ok, sup} = Task.Supervisor.start_link() 4 | 5 | state = %{ 6 | dir: download_dir, 7 | max_running: 5, 8 | parent: self(), 9 | pids: %{}, 10 | running: 0, 11 | session_id: session_id, 12 | supervisor: sup, 13 | work_ref: make_ref() 14 | } 15 | 16 | schedule_downloads(episodes, state) 17 | end 18 | 19 | defp schedule_downloads([], %{running: 0}), do: IO.puts "Finished downloading!" 20 | defp schedule_downloads(episode_queue, state) do 21 | if (state.running < state.max_running) and (length(episode_queue) > 0) do 22 | {:ok, episode_queue, state} = download_episode(episode_queue, state) 23 | schedule_downloads(episode_queue, state) 24 | else 25 | {:ok, episode_queue, state} = receive_download_msg(episode_queue, state) 26 | schedule_downloads(episode_queue, state) 27 | end 28 | end 29 | 30 | defp receive_download_msg(episode_queue, state) do 31 | work_ref = state.work_ref 32 | receive do 33 | {^work_ref, worker_pid, _work = {name, _url}, {:ok}} -> 34 | IO.puts "Finished #{name}" 35 | pids = Map.delete(state.pids, worker_pid) 36 | state = %{state | pids: pids, running: state.running - 1} 37 | {:ok, episode_queue, state} 38 | 39 | {^work_ref, worker_pid, _work = {name, _url}, {:redirect, location}} -> 40 | IO.puts "Redirect for #{name}" 41 | pids = Map.delete(state.pids, worker_pid) 42 | state = %{state | pids: pids, running: state.running - 1} 43 | new_work = {name, location} 44 | episode_queue = [new_work | episode_queue] 45 | {:ok, episode_queue, state} 46 | 47 | {^work_ref, worker_pid, _work = {name, _url}, {:error, reason}} -> 48 | IO.puts "Failed to download #{name}: #{reason}" 49 | pids = Map.delete(state.pids, worker_pid) 50 | state = %{state | pids: pids, running: state.running - 1} 51 | {:ok, episode_queue, state} 52 | 53 | {:DOWN, monitor_ref, _, worker_pid, reason} -> 54 | if Map.has_key?(state.pids, worker_pid) do 55 | Process.demonitor(monitor_ref) 56 | {_work = {name, _url}, pids} = Map.pop(state.pids, worker_pid) 57 | IO.puts "Failed to download #{name}: #{inspect reason}" 58 | state = %{state | pids: pids, running: state.running - 1} 59 | {:ok, episode_queue, state} 60 | else 61 | {:ok, episode_queue, state} 62 | end 63 | 64 | true -> 65 | {:ok, episode_queue, state} 66 | end 67 | end 68 | 69 | defp download_episode([work | queue], state) do 70 | {:ok, worker_pid} = spawn_worker(work, state) 71 | 72 | pids = Map.put(state.pids, worker_pid, work) 73 | state = %{state | pids: pids, running: state.running + 1} 74 | 75 | {:ok, queue, state} 76 | end 77 | 78 | defp spawn_worker(work = {name, _url}, state) do 79 | IO.puts "Starting download: #{name}" 80 | 81 | worker_state = Map.take(state, [:work_ref, :dir, :session_id, :parent]) 82 | 83 | {:ok, worker_pid} = Task.Supervisor.start_child( 84 | state.supervisor, SipsDownloader.EpisodeDownloader, :run, [work, worker_state] 85 | ) 86 | 87 | Process.monitor(worker_pid) 88 | 89 | {:ok, worker_pid} 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/sips_downloader/xml_parser.ex: -------------------------------------------------------------------------------- 1 | defmodule SipsDownloader.XMLParser do 2 | require Record 3 | 4 | Record.defrecord :xmlElement, Record.extract(:xmlElement, from_lib: "xmerl/include/xmerl.hrl") 5 | Record.defrecord :xmlAttribute, Record.extract(:xmlAttribute, from_lib: "xmerl/include/xmerl.hrl") 6 | Record.defrecord :xmlText, Record.extract(:xmlText, from_lib: "xmerl/include/xmerl.hrl") 7 | 8 | def parse_login_url(string) do 9 | string 10 | |> Floki.find("form") 11 | |> Floki.attribute("action") 12 | |> Enum.at(0) 13 | end 14 | 15 | def parse_episodes(feed) do 16 | feed 17 | |> to_xml 18 | |> episode_descriptions 19 | |> Enum.map(&normalize_string/1) 20 | |> Enum.map(&parse_episode_title_and_link/1) 21 | |> List.flatten 22 | end 23 | 24 | def to_xml(str) do 25 | String.replace(str, ~r/&.*;/, "") 26 | |> :erlang.binary_to_list 27 | |> :xmerl_scan.string 28 | |> elem(0) 29 | end 30 | 31 | def episode_descriptions(xml) do 32 | ~c{//channel/item/description/text()} 33 | |> :xmerl_xpath.string(xml) 34 | |> Enum.map(&(xmlText(&1, :value))) 35 | |> Enum.map(&normalize_string/1) 36 | end 37 | 38 | def normalize_string(string) do 39 | string 40 | |> to_string 41 | |> String.split(["\n"]) 42 | |> Enum.map(&String.strip/1) 43 | |> Enum.join("") 44 | end 45 | 46 | def parse_episode_title_and_link(episode_description) do 47 | attached_files_part = 48 | episode_description 49 | |> String.split("

Attached Files

") 50 | |> List.last 51 | 52 | episode_xml = to_xml("
" <> attached_files_part) 53 | 54 | link_elems = '//ul/li/a' |> :xmerl_xpath.string(episode_xml) 55 | 56 | Enum.map(link_elems, fn (link_elem) -> 57 | {get_link_title(link_elem), get_link_href(link_elem)} 58 | end) 59 | end 60 | 61 | def get_link_title(link_elem) do 62 | link_elem 63 | |> xmlElement(:content) 64 | |> List.first 65 | |> xmlText(:value) 66 | |> to_string 67 | |> String.strip 68 | end 69 | 70 | def get_link_href(link_elem) do 71 | link_elem 72 | |> xmlElement(:attributes) 73 | |> Enum.find(&(:href == xmlAttribute(&1, :name))) 74 | |> xmlElement(:content) 75 | |> to_string 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /misc/feed_without_description: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Elixir Sips 5 | https://elixirsips.dpdcart.com/ 6 | 7 | Wed, 01 Jul 2015 07:34:00 -0400 8 | josh.rubyist@gmail.com (Josh Adams) 9 | en 10 | Copyright 2015 Elixir Sips 11 | getdpd.com 12 | ElixirSips is a twice-weekly small dose of knowledge related to the Elixir programming language. 13 | 14 | <![CDATA[173 - Continuous Deployment]]> 15 | https://elixirsips.dpdcart.com/subscriber/post?id=821 16 | 17 | dpd-b49e1e04b7a47bee866c63837bb98c824e47225d 18 | Wed, 01 Jul 2015 07:34:00 -0400 19 | 20 | 21 | 22 | <![CDATA[172 - Arc File Uploads]]> 23 | https://elixirsips.dpdcart.com/subscriber/post?id=819 24 | 25 | dpd-36d06370d0b0796c87c6a6b56de85a2d66d780af 26 | Sat, 27 Jun 2015 09:22:00 -0400 27 | 28 | 29 | 30 | <![CDATA[171 - Erlang 18 and Time]]> 31 | https://elixirsips.dpdcart.com/subscriber/post?id=815 32 | 33 | dpd-9a2fa70dd941592721d62c52865406f54fb3af01 34 | Wed, 24 Jun 2015 12:42:00 -0400 35 | 36 | 37 | 38 | <![CDATA[170 - Building a Slack Bot]]> 39 | https://elixirsips.dpdcart.com/subscriber/post?id=810 40 | 41 | dpd-2981c3e60955bf57de1ee08d61839371e44fff58 42 | Fri, 19 Jun 2015 08:16:00 -0400 43 | 44 | 45 | 46 | <![CDATA[169 - Fractals, Part 2]]> 47 | https://elixirsips.dpdcart.com/subscriber/post?id=808 48 | 49 | dpd-a6df8eb71e88c7098b7cd81e4c41a950cc402917 50 | Wed, 17 Jun 2015 09:19:00 -0400 51 | 52 | 53 | 54 | <![CDATA[168 - Continuous Integration]]> 55 | https://elixirsips.dpdcart.com/subscriber/post?id=784 56 | 57 | dpd-e0e5332038b108ca27551d1198cd16e3e957b0b7 58 | Thu, 04 Jun 2015 22:49:00 -0400 59 | 60 | 61 | 62 | <![CDATA[167 - Implementing Logo, Part 2]]> 63 | https://elixirsips.dpdcart.com/subscriber/post?id=777 64 | 65 | dpd-e11c101e940fdf135fbf4a0c74c9dae37d0df2cf 66 | Tue, 02 Jun 2015 22:16:00 -0400 67 | 68 | Building a renderer for our Logo interpreter. 69 | 70 | 71 | <![CDATA[166 - Implementing Logo, Part 1]]> 72 | https://elixirsips.dpdcart.com/subscriber/post?id=776 73 | 74 | dpd-5446cc8d8c9ccd06bf00a85aec8ec4172950767e 75 | Sat, 30 May 2015 21:08:00 -0400 76 | 77 | Implementing the Logo graphical programming language in Elixir with wxWidgets. In this episode we just implement the underlying GenServer. 78 | 79 | 80 | <![CDATA[165 - Accounting 101]]> 81 | https://elixirsips.dpdcart.com/subscriber/post?id=774 82 | 83 | dpd-9c4e2499cd7e9d3da28502c6f053a96d6465fa88 84 | Fri, 29 May 2015 00:07:00 -0400 85 | 86 | Modeling a double-entry ledger. Terribly. And then discussing what went wrong. 87 | 88 | 89 | <![CDATA[164 - Fractals]]> 90 | https://elixirsips.dpdcart.com/subscriber/post?id=761 91 | 92 | dpd-156d0b4466f5b68ea81ece29014ae2b363c93538 93 | Thu, 21 May 2015 23:37:00 -0400 94 | 95 | Exploring L Systems, on our way to generating fractals. 96 | 97 | 98 | <![CDATA[163 - Building a Cron System, Part 3]]> 99 | https://elixirsips.dpdcart.com/subscriber/post?id=758 100 | 101 | dpd-845306ba77a2b1b2bbf126d526109caaecaa8278 102 | Wed, 13 May 2015 20:01:00 -0400 103 | 104 | Executing our jobs on their schedule. Bonus intro to defdelegate and ExUnit's `@tag`s. 105 | 106 | 107 | <![CDATA[162 - Building a Cron System, Part 2]]> 108 | https://elixirsips.dpdcart.com/subscriber/post?id=757 109 | 110 | dpd-b0aa6fa5d1b58b4a830006dc739b33b57dd29c30 111 | Fri, 08 May 2015 07:58:00 -0400 112 | 113 | Determining if a given time is matched by a Schedule. 114 | 115 | 116 | <![CDATA[161 - Building a Cron System, Part 1]]> 117 | https://elixirsips.dpdcart.com/subscriber/post?id=755 118 | 119 | dpd-ba144cb69da49b33a044f873ed42f458d38adabf 120 | Sat, 02 May 2015 10:45:00 -0400 121 | 122 | Building a parser for cron-style job definitions. 123 | 124 | 125 | <![CDATA[160 - MultiDef]]> 126 | https://elixirsips.dpdcart.com/subscriber/post?id=754 127 | 128 | dpd-ec5082c3c81960e8fccc25c52bacea299baa1e2b 129 | Thu, 30 Apr 2015 21:43:00 -0400 130 | 131 | A less-verbose way to define multiple function clauses, courtesy of Dave Thomas. 132 | 133 | 134 | <![CDATA[159 - Simple One for One Supervisors]]> 135 | https://elixirsips.dpdcart.com/subscriber/post?id=753 136 | 137 | dpd-db3cc80f439626ec5f75e295168642d6e07b3145 138 | Sun, 26 Apr 2015 00:01:00 -0400 139 | 140 | Getting a dynamically spawned process into a supervision tree to avoid orphaned processes, among other things. 141 | 142 | 143 | <![CDATA[158 - SOAP with Detergentex]]> 144 | https://elixirsips.dpdcart.com/subscriber/post?id=746 145 | 146 | dpd-434ea42ad6c8d1d06d216710d1992d37f0d16066 147 | Thu, 23 Apr 2015 16:06:00 -0400 148 | 149 | Using the `detergentex` library, which wraps the 150 | erlang `detergent` library, to interact with SOAP 151 | services in Elixir. 152 | 153 | 154 | <![CDATA[157 - Playing with the Code Module Part 1 - eval_string]]> 155 | https://elixirsips.dpdcart.com/subscriber/post?id=744 156 | 157 | dpd-d33a09418921d033fdcd444e29e2fc0731d2fc5b 158 | Thu, 16 Apr 2015 21:28:00 -0400 159 | 160 | Exploring the standard library's Code module's `eval_string` function for fun and whatnot. 161 | 162 | 163 | <![CDATA[156 - Interacting with Amazon's APIs with erlcloud]]> 164 | https://elixirsips.dpdcart.com/subscriber/post?id=742 165 | 166 | dpd-4a46f97061c681389ed4c3f0c4ba43b69ee66e9b 167 | Sat, 11 Apr 2015 18:54:00 -0400 168 | 169 | A comically brief overview of erlcloud and evidence that it does, in fact, work for me. 170 | 171 | 172 | <![CDATA[155 - OAuth2: Code Spelunking]]> 173 | https://elixirsips.dpdcart.com/subscriber/post?id=740 174 | 175 | dpd-da1d5954df47faa609204c615518928791d5da26 176 | Thu, 09 Apr 2015 21:43:00 -0400 177 | 178 | A dig through Sonny Scroggins' example of integrating a Phoenix application with GitHub via his OAuth2 package. 179 | 180 | 181 | <![CDATA[154 - inet]]> 182 | https://elixirsips.dpdcart.com/subscriber/post?id=733 183 | 184 | dpd-537587b7f62ed37c257b004087f8f868a78de9cd 185 | Wed, 01 Apr 2015 18:55:00 -0400 186 | 187 | Basic TCP/IP interactions built into Erlang's kernel application for you. 188 | 189 | 190 | <![CDATA[153 - Phoenix APIs and CORS]]> 191 | https://elixirsips.dpdcart.com/subscriber/post?id=732 192 | 193 | dpd-c66db07a4b4754fb5c48623fcff2eaa9f7b69e3a 194 | Tue, 31 Mar 2015 21:29:00 -0400 195 | 196 | Building a basic API with Phoenix, and allowing remote domains to access it from the browser. 197 | 198 | 199 | <![CDATA[152 - erlang_js]]> 200 | https://elixirsips.dpdcart.com/subscriber/post?id=730 201 | 202 | dpd-e03c6c229e258d43db66ba923630a788e8e77fd8 203 | Sat, 28 Mar 2015 15:57:00 -0400 204 | 205 | Evaluating javascript on a SpiderMonkey interpreter and getting back Erlang terms. 206 | 207 | 208 | <![CDATA[151 - System Information]]> 209 | https://elixirsips.dpdcart.com/subscriber/post?id=729 210 | 211 | dpd-9424815bed16a09dc9cff6261233a9cef58b64cc 212 | Sat, 28 Mar 2015 15:16:00 -0400 213 | 214 | Exploring a running Erlang system using tools from the shell. 215 | 216 | 217 | <![CDATA[150 - JInterface]]> 218 | https://elixirsips.dpdcart.com/subscriber/post?id=720 219 | 220 | dpd-45d429d6603673157a7cd2e6372304e55fc3ac58 221 | Tue, 17 Mar 2015 22:24:00 -0400 222 | 223 | Talking to Elixir from Java 224 | 225 | 226 | <![CDATA[149 - ejabberd Packet Filters]]> 227 | https://elixirsips.dpdcart.com/subscriber/post?id=719 228 | 229 | dpd-151b5f5d5075d3202fb7487f331ba85ad0430a0e 230 | Fri, 13 Mar 2015 21:05:00 -0400 231 | 232 | Modifying messages inside of our ejabberd server, specifically - making everyone yell at each other. 233 | 234 | 235 | <![CDATA[148 - ejabberd with Elixir]]> 236 | https://elixirsips.dpdcart.com/subscriber/post?id=718 237 | 238 | dpd-4c30ef69eaac733e69473a1f4668fa3d7e4316a7 239 | Thu, 12 Mar 2015 22:07:00 -0400 240 | 241 | Getting started interacting with a battle-hardened XMPP chat server by writing Elixir modules for it. 242 | 243 | 244 | <![CDATA[147 - webassembly]]> 245 | https://elixirsips.dpdcart.com/subscriber/post?id=716 246 | 247 | dpd-e1751af797c4f1307c68f71bba0e00b9a4c1a79b 248 | Fri, 06 Mar 2015 21:31:00 -0500 249 | 250 | Generate markup from blocks of elixir code, in the style of _why the lucky stiff's markaby. 251 | 252 | 253 | <![CDATA[146 - mailman]]> 254 | https://elixirsips.dpdcart.com/subscriber/post?id=714 255 | 256 | dpd-e803848bb958b19303529b4b071824ec69cba226 257 | Tue, 03 Mar 2015 23:44:00 -0500 258 | 259 | Easily send emails, parse emails, and verify email related features in your tests using this One Neat Trick. 260 | 261 | 262 | <![CDATA[145_good_times]]> 263 | https://elixirsips.dpdcart.com/subscriber/post?id=710 264 | 265 | dpd-eb535897309f8344fcea73020a27ddcdd6cb4caf 266 | Mon, 02 Mar 2015 14:23:00 -0500 267 | 268 | An elixir library for trivially specifying relative dates and times. 269 | 270 | 271 | <![CDATA[144 - Erlang's `calendar` module]]> 272 | https://elixirsips.dpdcart.com/subscriber/post?id=705 273 | 274 | dpd-f49a420e1fe7a98b74b854396f7e4a443a2af0e7 275 | Sun, 01 Mar 2015 14:31:00 -0500 276 | 277 | Interacting with Erlang's native date and time types. 278 | 279 | 280 | <![CDATA[143 - phoenix_generator]]> 281 | https://elixirsips.dpdcart.com/subscriber/post?id=703 282 | 283 | dpd-f41f618d39f1ecd5a003c8b2a3622c139a0c3554 284 | Fri, 20 Feb 2015 20:10:00 -0500 285 | 286 | A project that adds code generation to Phoenix for building models, controllers, templates, and channels. 287 | 288 | 289 | <![CDATA[142 - escript]]> 290 | https://elixirsips.dpdcart.com/subscriber/post?id=698 291 | 292 | dpd-beaeb46bd3c742812b96552df89b9e2521c7599d 293 | Tue, 17 Feb 2015 20:56:00 -0500 294 | 295 | Building quick 'binaries' after a fashion using Elixir's tooling. 296 | 297 | 298 | <![CDATA[141 - Set]]> 299 | https://elixirsips.dpdcart.com/subscriber/post?id=696 300 | 301 | dpd-6908aae4d850ee73b81ef2994a2c98b1bb01488e 302 | Wed, 11 Feb 2015 21:31:00 -0500 303 | 304 | Elixir ships with a Set behaviour, and a default HashSet implementation. 305 | 306 | 307 | <![CDATA[140 - gopher]]> 308 | https://elixirsips.dpdcart.com/subscriber/post?id=695 309 | 310 | dpd-2dfbeab1b57c04c1b95df9c248db85dfdacaa7e4 311 | Tue, 10 Feb 2015 21:35:00 -0500 312 | 313 | Using `goma`, which is essentially WebMachine for The Internet Gopher Protocol, to build an elixir-powered gopher server. 314 | 315 | 316 | <![CDATA[139 - hexdocs]]> 317 | https://elixirsips.dpdcart.com/subscriber/post?id=693 318 | 319 | dpd-87ef3d65563417c89fe166f34d92087bc9507d6c 320 | Thu, 05 Feb 2015 21:17:00 -0500 321 | 322 | Elixir's package manager, hex.pm, also provides a documentation server for elixir packages. We'll see how to push documentation up. 323 | 324 | 325 | <![CDATA[138 - Monitors and Links]]> 326 | https://elixirsips.dpdcart.com/subscriber/post?id=692 327 | 328 | dpd-d9f0a938bff2bc69e69de65ad890d51af68ff229 329 | Tue, 03 Feb 2015 20:37:00 -0500 330 | 331 | Two of the core building blocks that the Erlang VM provides. I'd neglected discussing them until now. Oops. 332 | 333 | 334 | <![CDATA[137 - Asterisk AGI with Elixir]]> 335 | https://elixirsips.dpdcart.com/subscriber/post?id=691 336 | 337 | dpd-7948abbc366cb961c1105a329949426ba607bfc4 338 | Sun, 01 Feb 2015 13:02:00 -0500 339 | 340 | Building out an erlagi handler module in Elixir that interacts with a remote HTTP API. 341 | 342 | 343 | <![CDATA[136 - Interfacing with Asterisk]]> 344 | https://elixirsips.dpdcart.com/subscriber/post?id=688 345 | 346 | dpd-f2c8a30c643e48b247bbd02f326617ef9edb2feb 347 | Sat, 31 Jan 2015 16:35:00 -0500 348 | 349 | Using the `erlagi` application from Elixir to control the logic of an extension in a PBX system via Asterisk's AGI protocol. 350 | 351 | 352 | <![CDATA[135 - Benchwarmer]]> 353 | https://elixirsips.dpdcart.com/subscriber/post?id=680 354 | 355 | dpd-bcc38614b2c6a209b36584b6ef895c758e4cd099 356 | Thu, 22 Jan 2015 20:23:00 -0500 357 | 358 | Another benchmarking tool, with a little bit less setup required. 359 | 360 | 361 | <![CDATA[134 - Elastic Search with Tirexs]]> 362 | https://elixirsips.dpdcart.com/subscriber/post?id=679 363 | 364 | dpd-d7d73146694311e2ba95ad13d61a3bcc80356e11 365 | Mon, 19 Jan 2015 21:04:00 -0500 366 | 367 | An easy way to interact with ElasticSearch. 368 | 369 | 370 | <![CDATA[133 - Boltun: Postgres Listen Notify]]> 371 | https://elixirsips.dpdcart.com/subscriber/post?id=678 372 | 373 | dpd-d1948a5a47138c34e463f8df906c62991c49037f 374 | Sat, 17 Jan 2015 21:34:00 -0500 375 | 376 | An Elixir package providing fantastically simple support for Postgres's built-in pubsub notifications. 377 | 378 | 379 | <![CDATA[132 - Randomness in the Erlang VM]]> 380 | https://elixirsips.dpdcart.com/subscriber/post?id=671 381 | 382 | dpd-e4ddf485e1b49e9a33f7e8e848edb265e5fd830c 383 | Tue, 13 Jan 2015 20:39:00 -0500 384 | 385 | An exploration of where our randomness in Tetris went so horribly wrong, along with an exposition 386 | of how to think about which process your code is running in, and why that's important. 387 | 388 | 389 | <![CDATA[131 - ExProf]]> 390 | https://elixirsips.dpdcart.com/subscriber/post?id=669 391 | 392 | dpd-5e3092780d751537d1e6d8660cdc24ce5e09bab5 393 | Wed, 07 Jan 2015 00:52:00 -0500 394 | 395 | An Elixir profiler, useful for finding where your program is spending its time. 396 | 397 | 398 | <![CDATA[130 - Extris Web Client]]> 399 | https://elixirsips.dpdcart.com/subscriber/post?id=663 400 | 401 | dpd-624c28e2f55980e246502e699e1b07cf2b2c66b2 402 | Sat, 27 Dec 2014 20:55:00 -0500 403 | 404 | Using Phoenix's websockets to build a JavaScript canvas-based renderer and interaction module for Extris games running in processes on the server. 405 | 406 | 407 | <![CDATA[129 - egd]]> 408 | https://elixirsips.dpdcart.com/subscriber/post?id=662 409 | 410 | dpd-1dcdd2c2d0b9773eed1bd43b88c26b6ea6973ae2 411 | Mon, 22 Dec 2014 20:04:00 -0500 412 | 413 | A module in the Erlang Standard Library meant for doing basic graphics. 414 | 415 | 416 | <![CDATA[128 - Managing Docker]]> 417 | https://elixirsips.dpdcart.com/subscriber/post?id=659 418 | 419 | dpd-9f8495ceada8026321e6ff0a2853c31cfd9eb241 420 | Sat, 20 Dec 2014 12:07:00 -0500 421 | 422 | Programmatically interacting with Docker using Elixir and erldocker. 423 | 424 | 425 | <![CDATA[127 - SSH Client Commands]]> 426 | https://elixirsips.dpdcart.com/subscriber/post?id=657 427 | 428 | dpd-e233171442d1ae0754eacc4696e939640b5bfe20 429 | Tue, 16 Dec 2014 21:03:00 -0500 430 | 431 | Becoming an SSH client and sending commands to an SSH server with Elixir. 432 | 433 | 434 | <![CDATA[126 - Piping Into Elixir]]> 435 | https://elixirsips.dpdcart.com/subscriber/post?id=654 436 | 437 | dpd-a87e5eb201b180640c670543ba086c7b7d4a9f72 438 | Tue, 09 Dec 2014 18:59:00 -0500 439 | 440 | Handling typical unix-script style stdin and stdout pipes trivially from your Elixir scripts. 441 | 442 | 443 | <![CDATA[125 - Dialyzer]]> 444 | https://elixirsips.dpdcart.com/subscriber/post?id=645 445 | 446 | dpd-ee99fa700b78a267fa1f8c55f8c01511456df35f 447 | Wed, 03 Dec 2014 23:33:00 -0500 448 | 449 | Letting the computer (rather than logs or users) tell you when you've written crap code. 450 | 451 | 452 | <![CDATA[124 - Typespecs]]> 453 | https://elixirsips.dpdcart.com/subscriber/post?id=643 454 | 455 | dpd-c00cd6d759f761b0a0b742de63f9c29a96a6f699 456 | Mon, 01 Dec 2014 07:59:00 -0500 457 | 458 | Layering type information over your functions to aid users and static analysis tools, and to catch errors. 459 | 460 | 461 | <![CDATA[123 - SDL Extris]]> 462 | https://elixirsips.dpdcart.com/subscriber/post?id=638 463 | 464 | dpd-927333737693cfb77a9706a96783b0b26e0a5224 465 | Tue, 25 Nov 2014 20:44:00 -0500 466 | 467 | Writing a renderer for the Tetris clone using SDL. 468 | 469 | 470 | <![CDATA[122 - SDL]]> 471 | https://elixirsips.dpdcart.com/subscriber/post?id=637 472 | 473 | dpd-e37cbee9e0fe7e7d1788f953d18f89b6aa406ce5 474 | Sat, 22 Nov 2014 17:16:00 -0500 475 | 476 | Using the SDL library for graphics from your Elixir programs. 477 | 478 | 479 | <![CDATA[121 - WxExtris, Part 8]]> 480 | https://elixirsips.dpdcart.com/subscriber/post?id=634 481 | 482 | dpd-4ee0aac740bec073ce1bb91ff3ff5929c5c828b7 483 | Thu, 20 Nov 2014 08:16:00 -0500 484 | 485 | Test-driven implementation of clearing full lines off of the board. 486 | 487 | 488 | <![CDATA[120 - WxExtris, Part 7]]> 489 | https://elixirsips.dpdcart.com/subscriber/post?id=631 490 | 491 | dpd-2f8286bdd29e9bb7d86e9676638a666c86a1ff8e 492 | Fri, 14 Nov 2014 21:37:00 -0500 493 | 494 | Introducing collision detection and cleaning up the renderer / game board data structure. 495 | 496 | 497 | <![CDATA[119 - WxExtris Part 6]]> 498 | https://elixirsips.dpdcart.com/subscriber/post?id=627 499 | 500 | dpd-6a46442be4571a93705954a582c5885a497ff685 501 | Thu, 06 Nov 2014 23:22:00 -0500 502 | 503 | Introducing multiple processes into the game, and running the render loop separately from the game logic. 504 | 505 | 506 | <![CDATA[118 - WxExtris Part 5]]> 507 | https://elixirsips.dpdcart.com/subscriber/post?id=624 508 | 509 | dpd-f233bbfc33aa85e7832a11426671183d2d95dc55 510 | Sun, 02 Nov 2014 23:18:00 -0500 511 | 512 | Defining the bottom game board boundary and providing the next shape when needed. 513 | 514 | 515 | <![CDATA[117: Wx Extris Part 4 - Testing]]> 516 | https://elixirsips.dpdcart.com/subscriber/post?id=622 517 | 518 | dpd-811a3aa5d8966503cc49ed3c9757055a5bcf9586 519 | Sun, 02 Nov 2014 21:23:00 -0500 520 | 521 | Writing some tests and implementing board boundary collision. 522 | 523 | 524 | <![CDATA[116 - Wx Extris Part 3: Refactoring]]> 525 | https://elixirsips.dpdcart.com/subscriber/post?id=619 526 | 527 | dpd-fef031dde7b866f23950072bea13e731c590177f 528 | Tue, 28 Oct 2014 21:04:00 -0400 529 | 530 | Turning a gigantic module into something a little more sensible and well-factored. 531 | 532 | 533 | <![CDATA[115 - Wx Extris Continued]]> 534 | https://elixirsips.dpdcart.com/subscriber/post?id=615 535 | 536 | dpd-9490d359b6f6480466e0d17fb0ceeb5025c70a7c 537 | Fri, 24 Oct 2014 07:41:00 -0400 538 | 539 | Making pieces fall and drawing the game board. 540 | 541 | 542 | <![CDATA[114 - Wx Keyboard Events]]> 543 | https://elixirsips.dpdcart.com/subscriber/post?id=613 544 | 545 | dpd-f3edf3f7c6d966f64791adb64d9e9cc6f3eca92a 546 | Sun, 19 Oct 2014 10:20:00 -0400 547 | 548 | Handling keyboard input in a wx application. 549 | 550 | 551 | <![CDATA[113 - Monitoring Network Traffic]]> 552 | https://elixirsips.dpdcart.com/subscriber/post?id=610 553 | 554 | dpd-7fe2b76fd43d84e2c2f074f626c6360885b422ab 555 | Tue, 14 Oct 2014 19:11:00 -0400 556 | 557 | Using libpcap from Elixir to see the stream of data passing across your computer's network interface. 558 | 559 | 560 | <![CDATA[112 - Benchfella]]> 561 | https://elixirsips.dpdcart.com/subscriber/post?id=608 562 | 563 | dpd-aa0ea81d95cc77daf72a85eb9a4d7b2cc14e32c5 564 | Fri, 10 Oct 2014 20:11:00 -0400 565 | 566 | A pleasant-to-use benchmarking tool that includes graph output. 567 | 568 | 569 | <![CDATA[111 - ExTwitter, Part 2]]> 570 | https://elixirsips.dpdcart.com/subscriber/post?id=604 571 | 572 | dpd-e8e5511796e5bc8f391ff65b4014adf8899e50f8 573 | Sun, 05 Oct 2014 21:34:00 -0400 574 | 575 | Streaming a Twitter search to the browser, because Phoenix Channels are meant to be paired with Elixir Streams, I promise. 576 | 577 | 578 | <![CDATA[110 - ExTwitter]]> 579 | https://elixirsips.dpdcart.com/subscriber/post?id=601 580 | 581 | dpd-c89075095b1d46d4a13e2d11958d01656caf19ae 582 | Sun, 28 Sep 2014 20:31:00 -0400 583 | 584 | A library for interacting with the Twitter API, using standard requests or the streaming responses. 585 | 586 | 587 | <![CDATA[109 - Socket]]> 588 | https://elixirsips.dpdcart.com/subscriber/post?id=598 589 | 590 | dpd-083ba8cf5050fa6ce7eeb0af9491f43cbcc0ff43 591 | Tue, 23 Sep 2014 08:53:00 -0400 592 | 593 | A pleasant abstraction around `gen_tcp`, `gen_udp`, `gen_sctp`, and `ssl`. 594 | 595 | 596 | <![CDATA[108 - Release Upgrades]]> 597 | https://elixirsips.dpdcart.com/subscriber/post?id=597 598 | 599 | dpd-c58f993cd7989a400eede8dd47a7af8d46a27d9f 600 | Mon, 22 Sep 2014 08:24:00 -0400 601 | 602 | Upgrading an application in-place without losing the running state. 603 | 604 | 605 | <![CDATA[107 - Phoenix Release]]> 606 | https://elixirsips.dpdcart.com/subscriber/post?id=593 607 | 608 | dpd-acf2a0f4cb183eb062a40a9598a9302c7a626f39 609 | Tue, 16 Sep 2014 21:39:00 -0400 610 | 611 | Using `exrm` to build a release of the BEAM Toolbox webapp, for deployment. 612 | 613 | 614 | <![CDATA[106 - Text Parsing]]> 615 | https://elixirsips.dpdcart.com/subscriber/post?id=587 616 | 617 | dpd-b8ac8701972a04a0cbd6b0c60bf492c2737bbbe0 618 | Wed, 03 Sep 2014 10:30:00 -0400 619 | 620 | Building a parser for a custom text format. 621 | 622 | 623 | <![CDATA[105 - Farkov Chain]]> 624 | https://elixirsips.dpdcart.com/subscriber/post?id=583 625 | 626 | dpd-c665b632229ab6dc424973039ae636f48e4890be 627 | Wed, 27 Aug 2014 21:50:00 -0400 628 | 629 | Generating killer marketing strategies with Elixir. 630 | 631 | 632 | <![CDATA[104: Ruby to Elixir via BERT-RPC]]> 633 | https://elixirsips.dpdcart.com/subscriber/post?id=580 634 | 635 | dpd-5111589d3b478af44902337fe7afe52576b60989 636 | Fri, 22 Aug 2014 22:31:00 -0400 637 | 638 | Trivial interop between Ruby and Elixir processes using BERT-RPC. 639 | 640 | 641 | <![CDATA[103 - BEAM Toolbox, Episode 11]]> 642 | https://elixirsips.dpdcart.com/subscriber/post?id=579 643 | 644 | dpd-5f317ac754021b12102cae9ba157bc2d809b5649 645 | Fri, 22 Aug 2014 21:02:00 -0400 646 | 647 | Switching to the expression style in Ecto, and using Phoenix's new routing helpers because we must. 648 | 649 | 650 | <![CDATA[102 - BEAM Toolbox, Episode 10]]> 651 | https://elixirsips.dpdcart.com/subscriber/post?id=575 652 | 653 | dpd-91f1b312424926a9a9a6b2426cb0a927dbd78102 654 | Thu, 14 Aug 2014 21:56:00 -0400 655 | 656 | Building a projects listing page, and reviewing some changes to the toolbox. 657 | 658 | 659 | <![CDATA[101 - BEAM Toolbox, Part 9]]> 660 | https://elixirsips.dpdcart.com/subscriber/post?id=573 661 | 662 | dpd-f81f297bd0350327a6e0c1faced14ab6811363a3 663 | Tue, 12 Aug 2014 22:17:00 -0400 664 | 665 | Building a CLI utility for categorizing our projects as a Mix task. 666 | 667 | 668 | <![CDATA[100 - BEAM Toolbox, Part 8]]> 669 | https://elixirsips.dpdcart.com/subscriber/post?id=570 670 | 671 | dpd-585b5ae36896368f777ef3195edb016d1ead6e91 672 | Sat, 09 Aug 2014 19:06:00 -0400 673 | 674 | Making the BEAM Toolbox a bit more useful, including adding in Disqus support. 675 | 676 | 677 | <![CDATA[099 - BEAM Toolbox, Part 7]]> 678 | https://elixirsips.dpdcart.com/subscriber/post?id=568 679 | 680 | dpd-02f5a698c7d6d087cdb5cd18d3b5cdcf85e7363b 681 | Wed, 06 Aug 2014 21:13:00 -0400 682 | 683 | Managing relationships in Ecto, and integrating Ecto into a Phoenix application. 684 | 685 | 686 | <![CDATA[098 - Contributing to Open Source]]> 687 | https://elixirsips.dpdcart.com/subscriber/post?id=565 688 | 689 | dpd-2fc64308b007410f03d937162254b10973ab220b 690 | Fri, 01 Aug 2014 21:26:00 -0400 691 | 692 | In which we contribute to the elixir codebase and open a pull request. 693 | 694 | 695 | <![CDATA[097 - BEAM Toolbox, Part 6]]> 696 | https://elixirsips.dpdcart.com/subscriber/post?id=564 697 | 698 | dpd-49e8f9751e2d5a3208fb6ffb842a523e7f46162c 699 | Fri, 01 Aug 2014 20:07:00 -0400 700 | 701 | Resurrecting the Toolbox with an Ecto-based data layer from scratch, and synchronizing data with the Hex API. 702 | 703 | 704 | <![CDATA[096 - Arduino Motor Driver]]> 705 | https://elixirsips.dpdcart.com/subscriber/post?id=560 706 | 707 | dpd-302ae6d30a495321152d507c8b317be4d435d738 708 | Thu, 24 Jul 2014 13:30:00 -0400 709 | 710 | An unusually formatted episode discussing an ad hoc serial protocol for controlling an Arduino Motor Controller. 711 | 712 | 713 | <![CDATA[095 - Link Extractor, Part 4]]> 714 | https://elixirsips.dpdcart.com/subscriber/post?id=558 715 | 716 | dpd-5a370d8efd28e8e2cc174a2b2449aad11fac37f1 717 | Sat, 19 Jul 2014 18:13:00 -0400 718 | 719 | Fixing some bugs and setting up a Zapier integration. 720 | 721 | 722 | <![CDATA[094 - Link Extractor, Part 3]]> 723 | https://elixirsips.dpdcart.com/subscriber/post?id=555 724 | 725 | dpd-883ff61ee202bcb7ba7da9fbae418e4bd3ae21a9 726 | Wed, 16 Jul 2014 08:18:00 -0400 727 | 728 | Deploying our application to a VPS from scratch and pushing in our first message. 729 | 730 | 731 | <![CDATA[093 - Link Extractor, Part 2]]> 732 | https://elixirsips.dpdcart.com/subscriber/post?id=551 733 | 734 | dpd-d9706ecb2eddc2f1265977c3767132d703e63b14 735 | Sat, 12 Jul 2014 10:07:00 -0400 736 | 737 | Using Phoenix to build an HTTP API for posting links into the system, and a page to view posted links. 738 | 739 | 740 | <![CDATA[092 - Link Extractor]]> 741 | https://elixirsips.dpdcart.com/subscriber/post?id=549 742 | 743 | dpd-4d7756a95e58a7151b033edbeb9ac9386df4d36e 744 | Wed, 09 Jul 2014 18:31:00 -0400 745 | 746 | Using poolboy to build a service for extracting data from a set of links and collecting them into memory for viewing later. 747 | 748 | 749 | <![CDATA[091 - Test Coverage]]> 750 | https://elixirsips.dpdcart.com/subscriber/post?id=546 751 | 752 | dpd-180544202457b1d326bf80666b28af22046a3e75 753 | Sun, 06 Jul 2014 20:02:00 -0400 754 | 755 | Exploring test coverage report generation, using both Erlang's `cover` application and the Coverex tool. 756 | 757 | 758 | <![CDATA[090 - Websockets Terminal]]> 759 | https://elixirsips.dpdcart.com/subscriber/post?id=544 760 | 761 | dpd-0fdc55a7a7013258f5a35f37fa503d09e73e0356 762 | Thu, 03 Jul 2014 23:44:00 -0400 763 | 764 | Taking advantage of Phoenix's native websockets support to build an in-browser bash terminal proxy. 765 | 766 | 767 | <![CDATA[089 - Protocol Buffers]]> 768 | https://elixirsips.dpdcart.com/subscriber/post?id=542 769 | 770 | dpd-b02d6c3df00731d250faf39354fe4a7095f9ee04 771 | Sun, 29 Jun 2014 19:39:00 -0400 772 | 773 | Easy cross-language serialization and deserialization of data structures using Google's Protocol Buffers and exprotobuf. 774 | 775 | 776 | <![CDATA[088 - Porcelain]]> 777 | https://elixirsips.dpdcart.com/subscriber/post?id=540 778 | 779 | dpd-5798eed6be9e1bf456e98152b373fc342ec95a2b 780 | Wed, 25 Jun 2014 19:32:00 -0400 781 | 782 | External Process interaction like the Port module but both simpler and more powerful. 783 | 784 | 785 | <![CDATA[087 - WordCloud]]> 786 | https://elixirsips.dpdcart.com/subscriber/post?id=536 787 | 788 | dpd-b3f1b7c7e02310104d87c390dddbf98cd266e7a7 789 | Fri, 20 Jun 2014 22:03:00 -0400 790 | 791 | Using Elixir to do a word frequency count on a corpus of text and generate input for a WordCloud generator. 792 | 793 | 794 | <![CDATA[086 - put_in and get_in]]> 795 | https://elixirsips.dpdcart.com/subscriber/post?id=534 796 | 797 | dpd-4b8e26be7785f03eafdc51bb0c0f47c64e8351ed 798 | Wed, 18 Jun 2014 20:13:00 -0400 799 | 800 | (and friends) Easy access to deeply nested data structures. 801 | 802 | 803 | <![CDATA[085 - Apex]]> 804 | https://elixirsips.dpdcart.com/subscriber/post?id=532 805 | 806 | dpd-12ef837138f05aff4c38e8236d52abdde5768a2a 807 | Fri, 13 Jun 2014 20:35:00 -0400 808 | 809 | Awesome Print for Elixir. An easy way to get insight into unfamiliar data structures. 810 | 811 | 812 | <![CDATA[084 - PCM Audio]]> 813 | https://elixirsips.dpdcart.com/subscriber/post?id=529 814 | 815 | dpd-a3f00fa2222695d5201010c1cfe11d6d636da20a 816 | Wed, 11 Jun 2014 20:40:00 -0400 817 | 818 | Generating PCM Audio and making some noise 819 | 820 | 821 | <![CDATA[083 - pg2]]> 822 | https://elixirsips.dpdcart.com/subscriber/post?id=526 823 | 824 | dpd-47d220d5ef4b985a9f07078a20d08a8462c21181 825 | Sat, 07 Jun 2014 14:49:00 -0400 826 | 827 | A replacement for the `pg` module that has some better semantics for distribution. 828 | 829 | 830 | <![CDATA[082 - Protocols]]> 831 | https://elixirsips.dpdcart.com/subscriber/post?id=525 832 | 833 | dpd-dcfd4eac814073147a5516503376089f77c5a9f8 834 | Thu, 05 Jun 2014 20:04:00 -0400 835 | 836 | A fantastic method for polymorphism in Elixir. Pairs well with structs. Slight oaky flavor. 837 | 838 | 839 | <![CDATA[081 - EEx]]> 840 | https://elixirsips.dpdcart.com/subscriber/post?id=520 841 | 842 | dpd-43db540c7570c6c7ed3c617a99240f0bbb1f243a 843 | Sat, 31 May 2014 20:37:00 -0400 844 | 845 | Elixir's built-in templating language, useful for embedding Elixir code inside of a string. 846 | 847 | 848 | <![CDATA[080 - erlubi]]> 849 | https://elixirsips.dpdcart.com/subscriber/post?id=518 850 | 851 | dpd-8132ce5c5d34243a225e1c82697de23d3bc624b9 852 | Tue, 27 May 2014 21:26:00 -0400 853 | 854 | A client for the Ubigraph visualization server, and an easy way to view a BEAM application's supervision tree in 3D. 855 | 856 | 857 | <![CDATA[079 - Tasks]]> 858 | https://elixirsips.dpdcart.com/subscriber/post?id=516 859 | 860 | dpd-87e63ba90c98728379af851919648fe07b7a40a6 861 | Fri, 23 May 2014 20:01:00 -0400 862 | 863 | An abstraction for computing a value in the background and receiving the value later. 864 | 865 | 866 | <![CDATA[078 - Agents]]> 867 | https://elixirsips.dpdcart.com/subscriber/post?id=514 868 | 869 | dpd-b2e65fcb8b6c7f0ab5b36a4d9ce38912d250ee4d 870 | Wed, 21 May 2014 08:41:00 -0400 871 | 872 | A simple abstraction around state. 873 | 874 | 875 | <![CDATA[077 - Exceptions and Errors]]> 876 | https://elixirsips.dpdcart.com/subscriber/post?id=511 877 | 878 | dpd-0baf24d09f1946547bcbbd3cb58b3bcee0a4472d 879 | Sat, 17 May 2014 19:30:00 -0400 880 | 881 | Manually raising exceptions, handling them, defining your own, and using them idiomatically. 882 | 883 | 884 | <![CDATA[076 - Streams]]> 885 | https://elixirsips.dpdcart.com/subscriber/post?id=510 886 | 887 | dpd-4553c702f0b65282255299c8889144327e829525 888 | Thu, 15 May 2014 19:15:00 -0400 889 | 890 | A brief overview of Elixir's 'lazy, composable Enumerables' 891 | 892 | 893 | <![CDATA[075 - DETS]]> 894 | https://elixirsips.dpdcart.com/subscriber/post?id=508 895 | 896 | dpd-4b31583fdd92d59e040252cb3d5b1570e62e6858 897 | Mon, 12 May 2014 20:17:00 -0400 898 | 899 | A disk-based version of ETS with a few restrictions. 900 | 901 | 902 | <![CDATA[074 - ETS]]> 903 | https://elixirsips.dpdcart.com/subscriber/post?id=506 904 | 905 | dpd-f152d5bdd1d5be150249695d77b4fca5a394c08e 906 | Thu, 08 May 2014 20:57:00 -0400 907 | 908 | Erlang term storage - a queryable database for your processes to store terms in. 909 | 910 | 911 | <![CDATA[073 - Process Dictionaries]]> 912 | https://elixirsips.dpdcart.com/subscriber/post?id=502 913 | 914 | dpd-f3f23a2777bb286c18290efd430e60f228c7e6ed 915 | Thu, 01 May 2014 18:39:00 -0400 916 | 917 | Process-local state that you likely should never use :) 918 | 919 | 920 | <![CDATA[072 - WxCalc, Part 2]]> 921 | https://elixirsips.dpdcart.com/subscriber/post?id=501 922 | 923 | dpd-d95d596e71b591965a5a2bcb28620aabd0ad89e1 924 | Wed, 30 Apr 2014 23:48:00 -0400 925 | 926 | Implementing our calculator's logic and wiring it up to wx events. 927 | 928 | 929 | <![CDATA[071 - Wx]]> 930 | https://elixirsips.dpdcart.com/subscriber/post?id=497 931 | 932 | dpd-a9cc177724158a781f328e595c039bb8bc2b2f07 933 | Fri, 25 Apr 2014 21:20:00 -0400 934 | 935 | Building a GUI calculator using Erlang's native wx bindings. 936 | 937 | 938 | <![CDATA[070 - Hex]]> 939 | https://elixirsips.dpdcart.com/subscriber/post?id=495 940 | 941 | dpd-e0cb5d7e0131590b491d2c9275de535b803756c8 942 | Thu, 24 Apr 2014 18:56:00 -0400 943 | 944 | Elixir's new package manager. 945 | 946 | 947 | <![CDATA[069 - Observer]]> 948 | https://elixirsips.dpdcart.com/subscriber/post?id=492 949 | 950 | dpd-c28fdc36d4fe5fd5474dff2c71630e66287db5f6 951 | Fri, 18 Apr 2014 21:54:00 -0400 952 | 953 | A graphical tool for inspection of a running Erlang/Elixir system and its processes. 954 | 955 | 956 | <![CDATA[068 - Port]]> 957 | https://elixirsips.dpdcart.com/subscriber/post?id=491 958 | 959 | dpd-e72209bdb29b4bff0ac78eb7ca979cc27c412b48 960 | Wed, 16 Apr 2014 22:17:00 -0400 961 | 962 | Interacting with external programs as if they were simple processes. 963 | 964 | 965 | <![CDATA[067 - Deploying to Heroku]]> 966 | https://elixirsips.dpdcart.com/subscriber/post?id=486 967 | 968 | dpd-6ebee8d2f0b706ca970a4367194f0127231575fc 969 | Fri, 11 Apr 2014 23:08:00 -0400 970 | 971 | Using HashNuke's Elixir buildpack for Heroku to deploy BEAM Toolbox. Also, a brief intro to ExConf. 972 | 973 | 974 | <![CDATA[066 - Plug.Static]]> 975 | https://elixirsips.dpdcart.com/subscriber/post?id=484 976 | 977 | dpd-4d63be468edc18b13f5aa6e8cb8dab4a7bf8262f 978 | Thu, 10 Apr 2014 08:48:00 -0400 979 | 980 | Serving static files without an opaque and complicated-looking cowboy handler. 981 | 982 | 983 | <![CDATA[065 - SSH]]> 984 | https://elixirsips.dpdcart.com/subscriber/post?id=482 985 | 986 | dpd-a658ffc1634b8bc86668e72bd37bfd6f5e73e03d 987 | Sun, 06 Apr 2014 22:29:00 -0400 988 | 989 | Using Erlang's `ssh` module to provide ssh access to a shell running your Elixir code. 990 | 991 | 992 | <![CDATA[064 - Digraph]]> 993 | https://elixirsips.dpdcart.com/subscriber/post?id=481 994 | 995 | dpd-d99967a1059c652a616cf9921718fd67080257fb 996 | Sun, 06 Apr 2014 19:48:00 -0400 997 | 998 | A look at using the `digraph`module from the Erlang standard library to implement pathfinding on a map. 999 | 1000 | 1001 | <![CDATA[063 - Tracing]]> 1002 | https://elixirsips.dpdcart.com/subscriber/post?id=474 1003 | 1004 | dpd-4e61ce171d447be49260aa7e2b150fa18f95897e 1005 | Thu, 27 Mar 2014 21:02:00 -0400 1006 | 1007 | A quick tour of the `dbg` module from Erlang, and its tracing features. 1008 | 1009 | 1010 | <![CDATA[062 - Quickie Synth]]> 1011 | https://elixirsips.dpdcart.com/subscriber/post?id=472 1012 | 1013 | dpd-0660032706dba19d1e9f0cf08d7cb3e37a21a744 1014 | Tue, 25 Mar 2014 22:44:00 -0400 1015 | 1016 | Using processes, gs, and shelling out to `sox` to build an Elixir-based synthesizer module. 1017 | 1018 | 1019 | <![CDATA[061 - Plug]]> 1020 | https://elixirsips.dpdcart.com/subscriber/post?id=469 1021 | 1022 | dpd-36c61c1cf1777823ac10c7606ccd7b5f75a67920 1023 | Sat, 22 Mar 2014 19:20:00 -0400 1024 | 1025 | Exploring Elixir's library for building composable web application modules. 1026 | 1027 | 1028 | <![CDATA[060 - New Style Comprehensions]]> 1029 | https://elixirsips.dpdcart.com/subscriber/post?id=467 1030 | 1031 | dpd-7322184862f713f43c6c8395259fc337d9153c22 1032 | Tue, 18 Mar 2014 22:32:00 -0400 1033 | 1034 | Elixir's list comprehensions have been entirely revamped, and made much more general. In this episode, we cover new style comprehensions and show off a few tricks they have that older style list comprehensions lacked. 1035 | 1036 | 1037 | <![CDATA[059 - Custom Mix Tasks]]> 1038 | https://elixirsips.dpdcart.com/subscriber/post?id=465 1039 | 1040 | dpd-73820b197f2fe7c911ffd1a6e6327bbef4ac578f 1041 | Sun, 16 Mar 2014 22:35:00 -0400 1042 | 1043 | Looking at building a basic mix task, and how you can go about testing it. 1044 | 1045 | 1046 | <![CDATA[058 - BEAM Toolbox, Part 5]]> 1047 | https://elixirsips.dpdcart.com/subscriber/post?id=462 1048 | 1049 | dpd-cce33fba9f7149ccba921db7d3dc8d2e751d6956 1050 | Thu, 13 Mar 2014 00:14:00 -0400 1051 | 1052 | Building a module that caches our GitHub data for a given project. 1053 | 1054 | 1055 | <![CDATA[057 - BEAM Toolbox, Part 4]]> 1056 | https://elixirsips.dpdcart.com/subscriber/post?id=461 1057 | 1058 | dpd-bb321df4c9d144a7344f2fdb61fb74694b61d882 1059 | Sun, 09 Mar 2014 21:59:00 -0400 1060 | 1061 | Moving our ad-hoc, tuple-based data layer to one powered by maps/structs. 1062 | 1063 | 1064 | <![CDATA[056 - Migrating Records To Maps]]> 1065 | https://elixirsips.dpdcart.com/subscriber/post?id=460 1066 | 1067 | dpd-0ef3f8fd496568b10e0d0d39c5d5e8934c0d3e6e 1068 | Sun, 09 Mar 2014 21:00:00 -0400 1069 | 1070 | Converting a production use of Records to use maps instead. 1071 | 1072 | 1073 | <![CDATA[055 - Maps, Part 2: Structs]]> 1074 | https://elixirsips.dpdcart.com/subscriber/post?id=454 1075 | 1076 | dpd-d1041c309aec57eed32a2ddae004a25b76051172 1077 | Fri, 28 Feb 2014 08:47:00 -0500 1078 | 1079 | Introducing structs and demonstrating how they can take the place of public Records. 1080 | 1081 | 1082 | <![CDATA[054 - Maps, Part 1]]> 1083 | https://elixirsips.dpdcart.com/subscriber/post?id=453 1084 | 1085 | dpd-4bf94deea259915d3c357b7588820bc5b3318f66 1086 | Thu, 27 Feb 2014 21:59:00 -0500 1087 | 1088 | An introduction to maps, a new feature in Erlang R17 and Elixir 0.13 1089 | 1090 | 1091 | <![CDATA[053 - BEAM Toolbox, Part 3]]> 1092 | https://elixirsips.dpdcart.com/subscriber/post?id=448 1093 | 1094 | dpd-55b3631f3c7a1da543dc917e68b1ae622dcce5d2 1095 | Thu, 20 Feb 2014 09:10:00 -0500 1096 | 1097 | Adding an integration testing layer that drives a browser using Hound, extracting a Controller helper, and adding Project pages. 1098 | 1099 | 1100 | <![CDATA[052 - BEAM Toolbox, Part 2]]> 1101 | https://elixirsips.dpdcart.com/subscriber/post?id=444 1102 | 1103 | dpd-cc87a225650e38932c583a9c9a6f14a40dba3e78 1104 | Sat, 15 Feb 2014 09:37:00 -0500 1105 | 1106 | Further work on the data layer, and introducing defoverridable. 1107 | 1108 | 1109 | <![CDATA[051 - BEAM Toolbox, Part 1]]> 1110 | https://elixirsips.dpdcart.com/subscriber/post?id=442 1111 | 1112 | dpd-b5a31a1c01922facab2dd79afa8f1dcd8f78d168 1113 | Wed, 12 Feb 2014 08:58:00 -0500 1114 | 1115 | Introducing BEAM Toolbox and starting work on the data layer. 1116 | 1117 | 1118 | <![CDATA[050 - Markdown Server, Part 5]]> 1119 | https://elixirsips.dpdcart.com/subscriber/post?id=439 1120 | 1121 | dpd-210f6831832d5b498f195a3b100c52c0f1373294 1122 | Thu, 06 Feb 2014 19:27:00 -0500 1123 | 1124 | Accepting environment variables for configuration and filtering out files we don't want. Plus, a call to action to build BEAM Toolbox. 1125 | 1126 | 1127 | <![CDATA[049 - Markdown Server, Part 4]]> 1128 | https://elixirsips.dpdcart.com/subscriber/post?id=434 1129 | 1130 | dpd-6a2c146afddf3c6d0aac75a41537080b0fbd0988 1131 | Sat, 01 Feb 2014 16:35:00 -0500 1132 | 1133 | Building a poor man's livereload system using inotify and websockets. 1134 | 1135 | 1136 | <![CDATA[048 - Markdown Server, Part 3]]> 1137 | https://elixirsips.dpdcart.com/subscriber/post?id=433 1138 | 1139 | dpd-8756e2da33bdff1224a927bb6725375ee4bf3065 1140 | Fri, 31 Jan 2014 21:35:00 -0500 1141 | 1142 | Using Eml for markup, and bringing in Compassand Zurb Foundation for styles. 1143 | 1144 | 1145 | <![CDATA[047 - Markdown Server, Part 2]]> 1146 | https://elixirsips.dpdcart.com/subscriber/post?id=429 1147 | 1148 | dpd-9eecb23975d8d108aea78a6cf6fac511e602b68d 1149 | Fri, 24 Jan 2014 23:20:00 -0500 1150 | 1151 | Writing an integration test suite and dealing with macros for the first time. 1152 | 1153 | 1154 | <![CDATA[046 - Markdown Server, Part 1]]> 1155 | https://elixirsips.dpdcart.com/subscriber/post?id=428 1156 | 1157 | dpd-13698c101c78abe21fabecbde8e98ad865cd2c93 1158 | Fri, 24 Jan 2014 19:35:00 -0500 1159 | 1160 | Building a HTTP Server to render Markdown files, using the Phoenix web framework. 1161 | 1162 | 1163 | <![CDATA[045 - Distribution, Part 2]]> 1164 | https://elixirsips.dpdcart.com/subscriber/post?id=424 1165 | 1166 | dpd-5b77eca5d0443e39ab5c5e4e495e4879af7daba9 1167 | Sat, 18 Jan 2014 10:02:00 -0500 1168 | 1169 | Sending messages to remote machines, and Joe Armstrong's favorite program. 1170 | 1171 | 1172 | <![CDATA[044 - Distribution]]> 1173 | https://elixirsips.dpdcart.com/subscriber/post?id=422 1174 | 1175 | dpd-db9a5eb0b78fe97ab2c8430979a97433124b2f26 1176 | Mon, 13 Jan 2014 21:55:00 -0500 1177 | 1178 | Sending messages from one Elixir node to another. 1179 | 1180 | 1181 | <![CDATA[043 - Releases With Relex]]> 1182 | https://elixirsips.dpdcart.com/subscriber/post?id=418 1183 | 1184 | dpd-d133276fb29a92296ec17f38cec53dfabe6816a6 1185 | Thu, 09 Jan 2014 07:46:00 -0500 1186 | 1187 | Making your Elixir application redistributable using Erlang releases. 1188 | 1189 | 1190 | <![CDATA[042 - File, Part 2]]> 1191 | https://elixirsips.dpdcart.com/subscriber/post?id=417 1192 | 1193 | dpd-c00b5d5fe3f67a42fa32b512cd5d481b72a52365 1194 | Tue, 07 Jan 2014 09:25:00 -0500 1195 | 1196 | In today's episode, we cover the second half of the File module. 1197 | 1198 | 1199 | <![CDATA[041 - File, Part 1]]> 1200 | https://elixirsips.dpdcart.com/subscriber/post?id=414 1201 | 1202 | dpd-499a0a8b85df049ab85293a4be963e699eb25287 1203 | Thu, 02 Jan 2014 08:52:00 -0500 1204 | 1205 | An overview of the first portion of the File module in the standard library. 1206 | 1207 | 1208 | <![CDATA[040 - Bitwise]]> 1209 | https://elixirsips.dpdcart.com/subscriber/post?id=413 1210 | 1211 | dpd-00de1169c5f8066cb354b4ad6d3872accb638452 1212 | Thu, 02 Jan 2014 08:10:00 -0500 1213 | 1214 | In this episode, we look at manipulating bits. This is typically useful for wire protocols. 1215 | 1216 | 1217 | <![CDATA[039 - Binaries: Deconstructing an IP Packet]]> 1218 | https://elixirsips.dpdcart.com/subscriber/post?id=411 1219 | 1220 | dpd-72bb8b378b48b17690f5e00ea5abc5761354b5b5 1221 | Fri, 27 Dec 2013 09:31:00 -0500 1222 | 1223 | In this episode, we use what we know about binary deconstruction to build a library to parse IP packets. 1224 | 1225 | 1226 | <![CDATA[038 - Binaries: Deconstructing and Constructing]]> 1227 | https://elixirsips.dpdcart.com/subscriber/post?id=409 1228 | 1229 | dpd-6b3e01c68dd4f87245ff2e5213242d90346c47f4 1230 | Thu, 26 Dec 2013 07:49:00 -0500 1231 | 1232 | Erlang provides extremely good tools for handling binaries, and Elixir benefits from them as well. In this episode, we look at how to construct and deconstruct binaries quickly. 1233 | 1234 | 1235 | <![CDATA[037 - ExLager]]> 1236 | https://elixirsips.dpdcart.com/subscriber/post?id=405 1237 | 1238 | dpd-56d26d6d1fbb5a7950dc729092911cdec2d9ac05 1239 | Thu, 19 Dec 2013 08:48:00 -0500 1240 | 1241 | Fantastic logging for your applications. 1242 | 1243 | 1244 | <![CDATA[036 - Weber, Part 2 - Performance]]> 1245 | https://elixirsips.dpdcart.com/subscriber/post?id=404 1246 | 1247 | dpd-261731b9e92d9ee9a3c86ff411b1a2537603fe92 1248 | Tue, 17 Dec 2013 16:25:00 -0500 1249 | 1250 | Building a simple cache, and a performance comparison with node.js and ruby. 1251 | 1252 | 1253 | <![CDATA[035 - Weber]]> 1254 | https://elixirsips.dpdcart.com/subscriber/post?id=399 1255 | 1256 | dpd-f2cb043205443522542977a84f173d74a5095b35 1257 | Thu, 12 Dec 2013 08:45:00 -0500 1258 | 1259 | Building a basic web app with the Weber MVC framework. 1260 | 1261 | 1262 | <![CDATA[034 - Elixiak]]> 1263 | https://elixirsips.dpdcart.com/subscriber/post?id=398 1264 | 1265 | dpd-65b2f632050407fd4525404b1282c579d6ed14b2 1266 | Mon, 09 Dec 2013 02:28:00 -0500 1267 | 1268 | Introduction to Elixiak - an ActiveRecord-like wrapper for Riak in Elixir. 1269 | 1270 | 1271 | <![CDATA[033 - Pry]]> 1272 | https://elixirsips.dpdcart.com/subscriber/post?id=396 1273 | 1274 | dpd-7cfb53216e3d6f1319bec0f1f878739e03dca03e 1275 | Sun, 08 Dec 2013 18:59:00 -0500 1276 | 1277 | Using IEx.pry to inspect and debug a running process. 1278 | 1279 | 1280 | <![CDATA[032 - Command Line Scripts]]> 1281 | https://elixirsips.dpdcart.com/subscriber/post?id=392 1282 | 1283 | dpd-a71b04315dd935bbc978fc0c48b2e6c4f8a8a663 1284 | Tue, 03 Dec 2013 08:13:00 -0500 1285 | 1286 | Basic Introduction to writing Elixir scripts that can be invoked and passed arguments from the command line. 1287 | 1288 | 1289 | <![CDATA[031 - TCP Servers]]> 1290 | https://elixirsips.dpdcart.com/subscriber/post?id=389 1291 | 1292 | dpd-ef4d8f3f5fc91b2757f9db478454889a62b1f2be 1293 | Thu, 28 Nov 2013 10:07:00 -0500 1294 | 1295 | Building network servers in Elixir using Erlang's `:gen_tcp` module. 1296 | 1297 | 1298 | <![CDATA[030 - ExActor]]> 1299 | https://elixirsips.dpdcart.com/subscriber/post?id=387 1300 | 1301 | dpd-b591602d8d685dfc5b65373c9eec81be2ba3533e 1302 | Tue, 26 Nov 2013 09:05:00 -0500 1303 | 1304 | Simplified generation of generic servers. 1305 | 1306 | 1307 | <![CDATA[029 - HTTP Clients]]> 1308 | https://elixirsips.dpdcart.com/subscriber/post?id=382 1309 | 1310 | dpd-9fce90acc12b203cf01876bb05427ee108630e6e 1311 | Thu, 21 Nov 2013 09:25:00 -0500 1312 | 1313 | A survey of the HTTP Client landscape. 1314 | 1315 | 1316 | <![CDATA[028 - Parsing XML]]> 1317 | https://elixirsips.dpdcart.com/subscriber/post?id=380 1318 | 1319 | dpd-4332e4d1b7b33b371e7b0d86f83862092c9f0b77 1320 | Tue, 19 Nov 2013 07:53:00 -0500 1321 | 1322 | A glimpse into XML parsing in Elixir, by way of interoperating with an Erlang module. 1323 | 1324 | 1325 | <![CDATA[027 - Dict, Part 2]]> 1326 | https://elixirsips.dpdcart.com/subscriber/post?id=377 1327 | 1328 | dpd-8fdb7bb8bce5bdf73f15457057d88bdc801e4374 1329 | Thu, 14 Nov 2013 19:14:00 -0500 1330 | 1331 | A brief overview of the second portion of the Dict module. 1332 | 1333 | 1334 | <![CDATA[026 - Dict, Part 1]]> 1335 | https://elixirsips.dpdcart.com/subscriber/post?id=376 1336 | 1337 | dpd-b2055c1c5f75983643083fdaeb9f6a4ed25a4a5c 1338 | Thu, 14 Nov 2013 19:10:00 -0500 1339 | 1340 | A brief overview of the first portion of the Dict module. 1341 | 1342 | 1343 | <![CDATA[025 - Ecto, Part 2: Dwitter]]> 1344 | https://elixirsips.dpdcart.com/subscriber/post?id=372 1345 | 1346 | dpd-290d62fd9b993b10aa73be0786505f57c4c9775e 1347 | Thu, 07 Nov 2013 22:20:00 -0500 1348 | 1349 | Overhauling our Dynamo webapp to use Ecto for its persistence layer. 1350 | 1351 | 1352 | <![CDATA[024 - Ecto, Part 1]]> 1353 | https://elixirsips.dpdcart.com/subscriber/post?id=370 1354 | 1355 | dpd-77f9f7e016cf01cd0868b6c56d03c0ff9e7816de 1356 | Tue, 05 Nov 2013 15:07:00 -0500 1357 | 1358 | A brief introduction to using Ecto with Postgres for persistence. 1359 | 1360 | 1361 | <![CDATA[023 - OTP, Part 5: Supervisors and Persistent State]]> 1362 | https://elixirsips.dpdcart.com/subscriber/post?id=367 1363 | 1364 | dpd-9ba61b7503e2a2d8c97d21be0bdc943e94e2818e 1365 | Fri, 01 Nov 2013 22:41:00 -0400 1366 | 1367 | Making a supervised process's state live through a crash. 1368 | 1369 | 1370 | <![CDATA[022 - OTP, Part 4: Supervisors]]> 1371 | https://elixirsips.dpdcart.com/subscriber/post?id=363 1372 | 1373 | dpd-4e48d92ef15a55278c611d3898036214ac98efb4 1374 | Tue, 29 Oct 2013 08:00:00 -0400 1375 | 1376 | Exploring OTP's Supervisors, and how they help us recover from crashes in processes. 1377 | 1378 | 1379 | <![CDATA[021 - ZeldaCat, Part 2]]> 1380 | https://elixirsips.dpdcart.com/subscriber/post?id=362 1381 | 1382 | dpd-676d292488550356f768d103eecdf164e1ad32ca 1383 | Fri, 25 Oct 2013 19:05:00 -0400 1384 | 1385 | We build a couple more components in the ZeldaCat game for more practice with GenEvent 1386 | 1387 | 1388 | <![CDATA[020 - OTP, Part 3 - GenEvent]]> 1389 | https://elixirsips.dpdcart.com/subscriber/post?id=361 1390 | 1391 | dpd-4c1da796dbd7b1ecb3709e70212dd3f3b934a90c 1392 | Wed, 23 Oct 2013 12:38:00 -0400 1393 | 1394 | An introduction to OTP's GenEvent 1395 | 1396 | 1397 | <![CDATA[019 - Enum, Part 3]]> 1398 | https://elixirsips.dpdcart.com/subscriber/post?id=359 1399 | 1400 | dpd-c393e93f2dcb42019ba8c0c2bc7b8aa5e9976141 1401 | Mon, 21 Oct 2013 11:02:00 -0400 1402 | 1403 | Wrapping up the Enum module 1404 | 1405 | 1406 | <![CDATA[018 - Enum, Part 2]]> 1407 | https://elixirsips.dpdcart.com/subscriber/post?id=350 1408 | 1409 | dpd-0f8303cdbc9ee941521caaf7b76731da0b3d4d19 1410 | Tue, 15 Oct 2013 23:06:00 -0400 1411 | 1412 | A brief overview of the second portion of the Enum module from the standard library 1413 | 1414 | 1415 | <![CDATA[017 - Enum, Part 1]]> 1416 | https://elixirsips.dpdcart.com/subscriber/post?id=348 1417 | 1418 | dpd-8049517948868c47e602daa299edb1dadd758da3 1419 | Sun, 13 Oct 2013 22:01:00 -0400 1420 | 1421 | A brief overview of the first portion of the Enum module from the standard library 1422 | 1423 | 1424 | <![CDATA[016 - Pipe Operator]]> 1425 | https://elixirsips.dpdcart.com/subscriber/post?id=344 1426 | 1427 | dpd-dc264ea5c3e0543c762c273788859c236e2a1dec 1428 | Tue, 08 Oct 2013 09:49:00 -0400 1429 | 1430 | Elixir supports the Pipe Operator - `|>` - which acts very similar to pipes in Unix. We'll dig into it. 1431 | 1432 | 1433 | <![CDATA[015 - OTP Part 2: Finite State Machines]]> 1434 | https://elixirsips.dpdcart.com/subscriber/post?id=341 1435 | 1436 | dpd-b9660be464972f6ba5dae7527063c62fc046317e 1437 | Fri, 04 Oct 2013 08:59:00 -0400 1438 | 1439 | An overview of the GenFSM OTP module and an example use case. 1440 | 1441 | 1442 | <![CDATA[014 - OTP Part 1: Servers]]> 1443 | https://elixirsips.dpdcart.com/subscriber/post?id=338 1444 | 1445 | dpd-5a5f721b54d22157b4be94f84e1f4532daa2de1c 1446 | Tue, 01 Oct 2013 16:15:00 -0400 1447 | 1448 | In this episode, we cover the basics of building a GenServer using OTP. 1449 | 1450 | 1451 | <![CDATA[013 - Processes, Part 2]]> 1452 | https://elixirsips.dpdcart.com/subscriber/post?id=332 1453 | 1454 | dpd-ded71a95e10e1d9b65825db7415a0b4c2bf3ec9e 1455 | Fri, 27 Sep 2013 00:24:00 -0400 1456 | 1457 | Modeling a BankAccount with a process 1458 | 1459 | 1460 | <![CDATA[012 - Processes]]> 1461 | https://elixirsips.dpdcart.com/subscriber/post?id=331 1462 | 1463 | dpd-dae0ac7be55f42aa7c2c813c1ee4a76485a473a8 1464 | Thu, 26 Sep 2013 22:29:00 -0400 1465 | 1466 | In today's episode, we're going to play with processes a bit to get a feel for how they can be used. 1467 | 1468 | 1469 | <![CDATA[011 - Records]]> 1470 | https://elixirsips.dpdcart.com/subscriber/post?id=324 1471 | 1472 | dpd-e76ebd6726108f35a5c8f75fd1b3561b01f16d82 1473 | Thu, 19 Sep 2013 09:02:00 -0400 1474 | 1475 | Just a quick rundown of Records in Elixir 1476 | 1477 | 1478 | <![CDATA[010 - List Comprehensions]]> 1479 | https://elixirsips.dpdcart.com/subscriber/post?id=323 1480 | 1481 | dpd-89b5f6b22a787a4f94b7fa3844d8c858149ecb73 1482 | Tue, 17 Sep 2013 08:46:00 -0400 1483 | 1484 | List Comprehensions are a nice feature for generating lists from other lists. In this episode, we cover the basics, and we test drive a module to generate a deck of cards. 1485 | 1486 | 1487 | <![CDATA[009 - Dynamo, Part 3]]> 1488 | https://elixirsips.dpdcart.com/subscriber/post?id=322 1489 | 1490 | dpd-a99258169e716dd101a33fae00e27a8955cbf660 1491 | Sun, 15 Sep 2013 21:15:00 -0400 1492 | 1493 | In Episode 9, we integrate the work we did in Dynamo with our experiments in Amnesia, to hook persistence into our Dwitter application. 1494 | 1495 | 1496 | <![CDATA[008 - Dynamo, Part 2]]> 1497 | https://elixirsips.dpdcart.com/subscriber/post?id=317 1498 | 1499 | dpd-00f8b115e467f028f97dff6ee4173c8a51c44a50 1500 | Tue, 10 Sep 2013 11:22:00 -0400 1501 | 1502 | The second part in the series on building Dwitter, a Twitter clone, using the Dynamo web framework. 1503 | 1504 | 1505 | <![CDATA[007 - Dynamo, Part 1]]> 1506 | https://elixirsips.dpdcart.com/subscriber/post?id=313 1507 | 1508 | dpd-070bcc897c0822994b5119f31bece510220f188c 1509 | Thu, 05 Sep 2013 12:45:00 -0400 1510 | 1511 | Building a basic web application in Elixir using Dynamo 1512 | 1513 | 1514 | <![CDATA[006 - Unit Testing]]> 1515 | https://elixirsips.dpdcart.com/subscriber/post?id=311 1516 | 1517 | dpd-4df77bde9eb54fd358b172a7606761a69f3cd17b 1518 | Tue, 03 Sep 2013 10:52:00 -0400 1519 | 1520 | This is an exceptionally long episode (21 minutes) on Unit Testing in Elixir. It's substantially longer than my intended episode length, but I think it's a very important topic so I'm willing to put up with the length. 1521 | 1522 | 1523 | <![CDATA[005 - Mix and Modules]]> 1524 | https://elixirsips.dpdcart.com/subscriber/post?id=298 1525 | 1526 | dpd-969472a9610dc652fd4076e9fe793c5abe669af5 1527 | Fri, 30 Aug 2013 08:57:00 -0400 1528 | 1529 | - Use mix to begin a new project 1530 | - Define a module 1531 | - Compile and use that module 1532 | - Examine a module's return value 1533 | - Document a module and its functions 1534 | - Generate nice HTML documentation output using ExDoc 1535 | 1536 | 1537 | <![CDATA[004 - Functions]]> 1538 | https://elixirsips.dpdcart.com/subscriber/post?id=284 1539 | 1540 | dpd-db84d34c2d16860fe1fc943d083ad82788e3acb9 1541 | Wed, 28 Aug 2013 08:00:00 -0400 1542 | 1543 | In Elixir, functions are first class types. Today we look at: Defining Anonymous Functions; Calling Anonymous Functions; Using Functions as first class types. 1544 | 1545 | 1546 | <![CDATA[003 - Pattern Matching]]> 1547 | https://elixirsips.dpdcart.com/subscriber/post?id=280 1548 | 1549 | dpd-3d393067166e1151725896b4a5dc0f89d886aa86 1550 | Fri, 23 Aug 2013 09:02:00 -0400 1551 | 1552 | An overview of Pattern Matching in Elixir 1553 | 1554 | 1555 | <![CDATA[002 - Basic Elixir]]> 1556 | https://elixirsips.dpdcart.com/subscriber/post?id=275 1557 | 1558 | dpd-44f4b90e9da71339292e198d7fd419ef2220d051 1559 | Mon, 19 Aug 2013 22:48:00 -0400 1560 | 1561 | A quick overview of some of the basic types in Elixir: Atoms, Numbers, Lists, Tuples, Regexes, and Booleans. 1562 | 1563 | 1564 | <![CDATA[001 - Introduction and Installing Elixir]]> 1565 | https://elixirsips.dpdcart.com/subscriber/post?id=271 1566 | 1567 | dpd-e902c86df08867fcb3693be8cb5dd3985aecd62b 1568 | Sun, 18 Aug 2013 22:10:00 -0400 1569 | 1570 | In the first episode ever, I introduce Elixir Sips and walk through installing Erlang and Elixir 1571 | 1572 | 1573 | 1574 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule SipsDownloader.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :sips_downloader, 6 | version: "0.2.2", 7 | name: "SipsDownloader", 8 | source_url: "https://github.com/DavsX/SipsDownloader", 9 | elixir: "~> 1.0", 10 | description: description, 11 | package: package, 12 | deps: deps, 13 | escript: escript] 14 | end 15 | 16 | defp description do 17 | """ 18 | Automatically download new ElixirSips episodes 19 | """ 20 | end 21 | 22 | defp package do 23 | [ 24 | maintainers: ["Dávid Kovács"], 25 | files: ["bin", "config", "lib", "mix.exs", "README*", "LICENSE*"], 26 | licenses: ["MIT"], 27 | links: %{"GitHub" => "https://github.com/DavsX/SipsDownloader"} 28 | ] 29 | end 30 | 31 | def application do 32 | [applications: [:httpoison]] 33 | end 34 | 35 | defp deps do 36 | [{:httpoison, "~> 0.7"}, 37 | {:floki, "~> 0.6"}, 38 | {:html_entities, "~> 0.2"}] 39 | end 40 | 41 | defp escript do 42 | [main_module: SipsDownloader] 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{"floki": {:hex, :floki, "0.6.0"}, 2 | "hackney": {:hex, :hackney, "1.3.2"}, 3 | "html_entities": {:hex, :html_entities, "0.2.1"}, 4 | "httpoison": {:hex, :httpoison, "0.7.4"}, 5 | "idna": {:hex, :idna, "1.0.2"}, 6 | "mochiweb": {:hex, :mochiweb, "2.12.2"}, 7 | "ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.5"}} 8 | -------------------------------------------------------------------------------- /test/feed: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Elixir Sips 5 | https://elixirsips.dpdcart.com/ 6 | 7 | Wed, 01 Jul 2015 07:34:00 -0400 8 | josh.rubyist@gmail.com (Josh Adams) 9 | en 10 | Copyright 2015 Elixir Sips 11 | getdpd.com 12 | ElixirSips is a twice-weekly small dose of knowledge related to the Elixir programming language. 13 | 14 | <![CDATA[172 - Continuous Deployment]]> 15 | https://elixirsips.dpdcart.com/subscriber/post?id=821 16 | 18 |
19 |

Episode 173: Deploying Phoenix Applications to Heroku

20 | 27 |
28 |

Attached Files

29 | 41 |
42 | ]]> 43 | dpd-b49e1e04b7a47bee866c63837bb98c824e47225d 44 | Wed, 01 Jul 2015 07:34:00 -0400 45 | 46 | 47 | 48 | <![CDATA[173 - Continuous Deployment]]> 49 | https://elixirsips.dpdcart.com/subscriber/post?id=821 50 | 52 |
53 |

Episode 173: Deploying Phoenix Applications to Heroku

54 | 61 |
62 |

Attached Files

63 | 75 | 76 | ]]>
77 | dpd-b49e1e04b7a47bee866c63837bb98c824e47225d 78 | Wed, 01 Jul 2015 07:34:00 -0400 79 | 80 |
81 | 82 | 83 | -------------------------------------------------------------------------------- /test/sips_downloader_test.exs: -------------------------------------------------------------------------------- 1 | defmodule SipsDownloaderTest do 2 | use ExUnit.Case 3 | 4 | setup do 5 | feed = File.read!("test/feed") 6 | {:ok, feed: feed} 7 | end 8 | 9 | test "processing feed", %{feed: feed} do 10 | links = SipsDownloader.FeedParser.parse_episodes(feed) 11 | expected = [{"Link1_title.mp4", "Link1_href"}, 12 | {"Link2_title.mp4", "Link2_href"}, 13 | {"Link3_title.mp4", "Link3_href"}, 14 | {"Link4_title.mp4", "Link4_href"}] 15 | 16 | assert links == expected 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------