Skip to content

Commit

Permalink
Merge branch 'master' into hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
davejlong committed Dec 3, 2016
2 parents 41eb3b7 + 55fe04b commit c30575c
Show file tree
Hide file tree
Showing 16 changed files with 544 additions and 41 deletions.
24 changes: 24 additions & 0 deletions .ebert.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This configuration was used Ebert to review the kittoframework/kitto repository
# on 0d690330dfb1d4fc4c03f007dddfbaaaeead1efa.
# You can make this the default configuration for future reviews by moving this
# file to your repository as `.ebert.yml` and pushing it to GitHub, and tweak
# it as you wish - To know more on how to change this file to better review your
# repository you can go to https://ebertapp.io/docs/config and see the configuration
# details.
---
styleguide: plataformatec/linters
engines:
credo:
enabled: true
fixme:
enabled: true
eslint:
enabled: false
scss-lint:
enabled: true
remark-lint:
enabled: true
exclude_paths:
- config
- test

43 changes: 43 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Kitto Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [dimitrisplusplus@gmail.com](mailto:dimitrisplusplus@gmail.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org/), version 1.4, available at [http://contributor-covenant.org/version/1/4]().
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,17 @@ end
The above will spawn a supervised process which will emit a [server-sent
event](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) with the name `random` every second.

Jobs can also run commands on the server. Data broadcast using commands is in
the form `{exit_code: integer, stdout: String.t}`. For example the following
job will broadcast a `kitto_last_commit` event with the results of the `curl`
statement:

```elixir
job :kitto_last_commit,
every: {5, :minutes},
command: "curl https://api.github.com/repos/kittoframework/kitto/commits\?page\=1\&per_page\=1"
```

## Hooks

If, instead of polling for new data from a data source, you want to act on data
Expand Down
22 changes: 11 additions & 11 deletions installer/templates/new/assets/javascripts/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ class Widget extends React.Component {
}

static events() {
if (!this._events) {
this._events = new EventSource(`/events?topics=${this.sources().join()}`);
if (this._events) { return this._events; }

this._events.addEventListener('error', (e) => {
let state = e.currentTarget.readyState;
this._events = new EventSource(`/events?topics=${this.sources().join()}`);

if (state === EventSource.CONNECTING || state === EventSource.CLOSED) {
this._events.addEventListener('error', (e) => {
let state = e.currentTarget.readyState;

// Restart the dashboard
setTimeout((() => window.location.reload()), 5 * 60 * 1000)
}
});
if (state === EventSource.CONNECTING || state === EventSource.CLOSED) {

this.bindInternalEvents();
}
// Restart the dashboard
setTimeout((() => window.location.reload()), 5 * 60 * 1000)
}
});

this.bindInternalEvents();

return this._events;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/kitto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ defmodule Kitto do
defp port(_), do: @defaults.port

defp children(:dev) do
case Application.get_env(:kitto, :reload_code?, true) do
case Kitto.CodeReloader.reload_code? do
true -> children(:all) ++ [worker(Kitto.CodeReloader, [[server: :runner]])]
false -> children(:all)
end
Expand Down
41 changes: 31 additions & 10 deletions lib/kitto/code_reloader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,40 @@ defmodule Kitto.CodeReloader do

alias Kitto.Runner

@doc """
Starts the code reloader server
"""
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: opts[:name] || __MODULE__)
end

@doc false
def init(opts) do
Application.ensure_all_started(:fs)
:fs.subscribe
if reload_code? do
Application.ensure_all_started(:fs)
:fs.subscribe
end

{:ok, %{opts: opts}}
end

@doc """
Returns true when the code reloader is set to start
See: https://github.com/kittoframework/kitto/wiki/Code-Reloading
"""
def reload_code? do
Application.get_env(:kitto, :reload_code?, true) && Mix.env == :dev
end

### Callbacks

# Linux inotify
def handle_info({_pid, {:fs, :file_event}, {path, [:modified, _]}}, state) do
reload(path, state)
end
def handle_info({_pid, {:fs, :file_event}, {path, event}}, state)
when event in [[:modified, :closed], [:created]],
do: reload(path, state)

def handle_info({_pid, {:fs, :file_event}, {path, [:deleted]}}, state),
do: stop(path, state)

# Mac fsevent
def handle_info({_pid, {:fs, :file_event}, {path, [_, :modified]}}, state) do
Expand All @@ -34,10 +51,18 @@ defmodule Kitto.CodeReloader do
{:noreply, state}
end

defp stop(path, state) do
with file <- path |> to_string do
if job?(file), do: Runner.stop_job(state.opts[:server], file)
end

{:noreply, state}
end

defp reload(path, state) do
with file <- path |> to_string do
cond do
file |> job? -> reload(:job, state.opts[:server], file)
file |> job? -> Runner.reload_job(state.opts[:server], file)
file |> lib? -> Mix.Tasks.Compile.Elixir.run ["--ignore-module-conflict"]
true -> :noop # File not watched.
end
Expand All @@ -46,10 +71,6 @@ defmodule Kitto.CodeReloader do
{:noreply, state}
end

defp reload(:job, server, file) do
server |> Process.whereis |> Runner.reload_job(file)
end

defp jobs_rexp, do: ~r/#{Kitto.Runner.jobs_dir}.+.*exs?$/
defp lib_rexp, do: ~r/#{Kitto.root}\/lib.+.*ex$/

Expand Down
51 changes: 43 additions & 8 deletions lib/kitto/job/dsl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ defmodule Kitto.Job.DSL do
A DSL to define jobs populating the widgets with data.
"""

alias Kitto.Job
alias Kitto.Notifier

@doc false
defmacro __using__(_opts) do
quote do
Expand All @@ -14,8 +17,13 @@ defmodule Kitto.Job.DSL do
@doc """
Main API to define jobs.
It accepts an expression representing data retrieval and any transformations
required to broadcast events to the widgets.
Jobs can either be defined with a block or a command. When using a block, the
expression represents data retrieval and any transformations required to
broadcast events to the widgets. With command, the stdout and exit code of
the command will be broadcasted to the widgets using the jobs name as the
data source.
Data broadcast using commands is in the form `{exit_code: integer, stdout: String.t}`
## Examples
Expand All @@ -29,19 +37,46 @@ defmodule Kitto.Job.DSL do
job :twitter, do: Twitter.stream("#elixir", &(broadcast!(:twitter, &1))
job :echo, every: :minute, command: "echo hello"
job :kitto_last_commit,
every: {5, :minutes},
command: "curl https://api.github.com/repos/kittoframework/kitto/commits\?page\=1\&per_page\=1"
## Options
* `:every` - Sets the interval on which the job will be performed. When it's not
specified, the job will be called once (suitable for streaming resources).
* `:first_at` - A timeout after which to perform the job for the first time
* `:command` - A command to be run on the server which will automatically
broadcast events using the jobs name.
"""
defmacro job(name, options, contents \\ []) do
defmacro job(name, options, do: block) do
quote do
Kitto.Job.register binding[:runner_server],
unquote(name),
unquote(options),
(__ENV__ |> Map.take([:file, :line])),
fn -> unquote(contents[:do]) end
Job.register binding[:runner_server],
unquote(name),
unquote(options),
(__ENV__ |> Map.take([:file, :line])),
fn -> unquote(block) end
end
end

defmacro job(name, options) do
quote do
command = unquote(options)[:command]
block = fn ->
[sh | arguments] = command |> String.split
{stdout, exit_code} = System.cmd(sh, arguments)

Notifier.broadcast!(unquote(name), %{stdout: stdout, exit_code: exit_code})
end
Job.register binding[:runner_server],
unquote(name),
unquote(options),
(__ENV__ |> Map.take([:file, :line])),
block
end
end
end
19 changes: 16 additions & 3 deletions lib/kitto/runner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,19 @@ defmodule Kitto.Runner do
end

@doc """
Reloads all jobs defined in the given fil
Reloads all jobs defined in the given file
"""
def reload_job(server, file) do
GenServer.cast(server, {:reload_job, file})
end

@doc """
Stops all jobs defined in the given file
"""
def stop_job(server, file) do
GenServer.cast(server, {:stop_job, file})
end

@doc """
Returns all the registered jobs
"""
Expand Down Expand Up @@ -74,7 +81,7 @@ defmodule Kitto.Runner do
def handle_cast({:reload_job, file}, state) do
Logger.info "Reloading job file: #{file}"

jobs = stop_job(state, file)
jobs = stop_jobs(state, file)

server = self
spawn fn ->
Expand All @@ -88,6 +95,12 @@ defmodule Kitto.Runner do
{:noreply, %{state | jobs: jobs}}
end

def handle_cast({:stop_job, file}, state) do
Logger.info "Stoppping jobs in file: #{file}"

{:noreply, %{state | jobs: stop_jobs(state, file)}}
end

defp jobs_in_file(jobs, file) do
jobs |> Enum.filter(fn %{definition: %{file: f}} -> f == file end)
end
Expand All @@ -107,7 +120,7 @@ defmodule Kitto.Runner do
end
end

defp stop_job(state, file) do
defp stop_jobs(state, file) do
state.jobs
|> jobs_in_file(file)
|> Enum.reduce(state.jobs, fn (job, jobs) ->
Expand Down

0 comments on commit c30575c

Please sign in to comment.