clockface 1.0.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +210 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/config/clockface_manifest.js +2 -0
  6. data/app/assets/images/clockface/clockface.svg +34 -0
  7. data/app/assets/javascripts/clockface/application.js +17 -0
  8. data/app/assets/javascripts/clockface/flash.js +7 -0
  9. data/app/assets/javascripts/clockface/sorttable.js +494 -0
  10. data/app/assets/stylesheets/clockface/application.scss +80 -0
  11. data/app/assets/stylesheets/clockface/application/_fonts.scss +2 -0
  12. data/app/assets/stylesheets/clockface/application/colors.scss +8 -0
  13. data/app/assets/stylesheets/clockface/application/flash.scss +6 -0
  14. data/app/assets/stylesheets/clockface/application/footer.scss +37 -0
  15. data/app/assets/stylesheets/clockface/application/nav.scss +51 -0
  16. data/app/assets/stylesheets/clockface/events/delete.scss +45 -0
  17. data/app/assets/stylesheets/clockface/events/event_form.scss +62 -0
  18. data/app/assets/stylesheets/clockface/events/index.scss +56 -0
  19. data/app/assets/stylesheets/clockface/tasks/delete.scss +29 -0
  20. data/app/assets/stylesheets/clockface/tasks/index.scss +47 -0
  21. data/app/assets/stylesheets/clockface/tasks/task_form.scss +20 -0
  22. data/app/controllers/clockface/application_controller.rb +20 -0
  23. data/app/controllers/clockface/events_controller.rb +151 -0
  24. data/app/controllers/clockface/root_controller.rb +7 -0
  25. data/app/controllers/clockface/tasks_controller.rb +137 -0
  26. data/app/events/clockface/application_job.rb +4 -0
  27. data/app/helpers/clockface/application_helper.rb +4 -0
  28. data/app/helpers/clockface/config_helper.rb +32 -0
  29. data/app/helpers/clockface/events_helper.rb +37 -0
  30. data/app/helpers/clockface/logging_helper.rb +12 -0
  31. data/app/mailers/clockface/application_mailer.rb +6 -0
  32. data/app/models/clockface/application_record.rb +7 -0
  33. data/app/models/clockface/event.rb +179 -0
  34. data/app/models/clockface/task.rb +12 -0
  35. data/app/presenters/clockface/events_presenter.rb +48 -0
  36. data/app/services/clockface/event_validation_interactor.rb +35 -0
  37. data/app/services/clockface/task_validation_interactor.rb +25 -0
  38. data/app/views/clockface/application/_flash.html.erb +25 -0
  39. data/app/views/clockface/application/_footer.html.erb +15 -0
  40. data/app/views/clockface/application/_nav.html.erb +19 -0
  41. data/app/views/clockface/events/_event_form.html.erb +130 -0
  42. data/app/views/clockface/events/delete.html.erb +124 -0
  43. data/app/views/clockface/events/edit.html.erb +14 -0
  44. data/app/views/clockface/events/index.html.erb +108 -0
  45. data/app/views/clockface/events/new.html.erb +14 -0
  46. data/app/views/clockface/tasks/_task_form.html.erb +57 -0
  47. data/app/views/clockface/tasks/delete.html.erb +83 -0
  48. data/app/views/clockface/tasks/edit.html.erb +14 -0
  49. data/app/views/clockface/tasks/index.html.erb +70 -0
  50. data/app/views/clockface/tasks/new.html.erb +14 -0
  51. data/app/views/layouts/clockface/application.html.erb +27 -0
  52. data/config/locales/en.yml +158 -0
  53. data/config/routes.rb +15 -0
  54. data/db/migrate/20170528230549_create_clockface_tasks.rb +10 -0
  55. data/db/migrate/20170528234810_create_clockface_events.rb +20 -0
  56. data/lib/clockface.rb +135 -0
  57. data/lib/clockface/engine.rb +79 -0
  58. data/lib/clockface/version.rb +3 -0
  59. data/lib/clockwork/database_events/synchronizer.rb +73 -0
  60. data/lib/tasks/clockface_tasks.rake +4 -0
  61. metadata +199 -0
@@ -0,0 +1,12 @@
1
+ module Clockface
2
+ class Task < ApplicationRecord
3
+ has_many(
4
+ :events,
5
+ foreign_key: "clockface_task_id",
6
+ class_name: "Clockface::Event"
7
+ )
8
+
9
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
10
+ validates :command, presence: true
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ module Clockface
2
+ class EventsPresenter < SimpleDelegator
3
+ include ConfigHelper
4
+
5
+ def period
6
+ I18n.t(
7
+ "datetime.distance_in_words.x_#{event.period_units}",
8
+ count: event.period_value
9
+ )
10
+ end
11
+
12
+ def at
13
+ at = event.at
14
+
15
+ # `at` uses the day name from the ruby standard library - Date::DAYNAMES
16
+ # Here we replace that with the translated version for any given locale
17
+ if event.day_of_week.present?
18
+ at.gsub!(
19
+ Date::DAYNAMES[event.day_of_week],
20
+ I18n.t("date.day_names")[event.day_of_week]
21
+ )
22
+ end
23
+
24
+ at
25
+ end
26
+
27
+ def if_condition
28
+ return if event.if_condition.blank?
29
+
30
+ Clockface::Event.
31
+ human_attribute_name("if_condition.#{event.if_condition}")
32
+ end
33
+
34
+ def last_triggered_at
35
+ return unless event.last_triggered_at
36
+
37
+ event.last_triggered_at.
38
+ in_time_zone(clockface_time_zone).
39
+ strftime(I18n.t("datetime.formats.international"))
40
+ end
41
+
42
+ private
43
+
44
+ def event
45
+ __getobj__
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ module Clockface
2
+ class EventValidationInteractor
3
+ include Interactor
4
+
5
+ after { context.fail! if context.errors.any? }
6
+
7
+ def call
8
+ context.errors = []
9
+
10
+ handle_invalid_model unless model_valid?
11
+ handle_duplicate_event if duplicate_event?
12
+ end
13
+
14
+ private
15
+
16
+ def model_valid?
17
+ context.event.valid?
18
+ end
19
+
20
+ def duplicate_event?
21
+ Clockface::Event.find_duplicates_of(context.event).any?
22
+ end
23
+
24
+ def handle_invalid_model
25
+ context.event.errors.messages.each_value do |messages|
26
+ context.errors << messages.first
27
+ end
28
+ end
29
+
30
+ def handle_duplicate_event
31
+ context.errors <<
32
+ I18n.t("clockface.events.#{context.action}.duplicate_event")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ module Clockface
2
+ class TaskValidationInteractor
3
+ include Interactor
4
+
5
+ after { context.fail! if context.errors.any? }
6
+
7
+ def call
8
+ context.errors = []
9
+
10
+ handle_invalid_model unless model_valid?
11
+ end
12
+
13
+ private
14
+
15
+ def model_valid?
16
+ context.task.valid?
17
+ end
18
+
19
+ def handle_invalid_model
20
+ context.task.errors.messages.each_value do |messages|
21
+ context.errors << messages.first
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ <% flash.each do |flash_type, message| %>
2
+ <%
3
+ flash_class = {
4
+ success: "alert-success",
5
+ error: "alert-danger",
6
+ alert: "alert-warning",
7
+ notice: "alert-info"
8
+ }[flash_type.to_sym] || flash_type.to_s
9
+ %>
10
+
11
+ <%
12
+ formatted_message =
13
+ if message.is_a?(Array)
14
+ content_tag(:ul) do
15
+ message.each { |item| concat content_tag(:li, item) }
16
+ end
17
+ else
18
+ message
19
+ end
20
+ %>
21
+ <div class="flash active alert <%= flash_class %> fade in">
22
+ <button class="flash__close close" data-dismiss="alert">x</button>
23
+ <%= formatted_message.html_safe %>
24
+ </div>
25
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <div class="application-footer">
2
+ <div class="application-footer__need-help">
3
+ <i>
4
+ <%= t(".need_help_html", href: "https://stackoverflow.com/questions/tagged/clockface") %>
5
+ </i>
6
+ </div>
7
+ <div class="application-footer__version">
8
+ v<%= Clockface::VERSION %>
9
+ </div>
10
+ <%= link_to "https://gitlab.com/abhchand/clockface", class: "application-footer__source-link", target: "_blank" do %>
11
+ <div class="application-footer__source-btn">
12
+ &lt;/&gt;
13
+ </div>
14
+ <% end %>
15
+ </div>
@@ -0,0 +1,19 @@
1
+ <nav class="application-nav navbar navbar-default container">
2
+ <div class="container-fluid">
3
+ <div class="navbar-header">
4
+ <a class="application-nav__icon navbar-brand" href="<%= clockface.root_path %>">
5
+ <%= inline_svg "clockface/clockface.svg", height: "50", width: "50" %>
6
+ </a>
7
+ <div class="application-nav__heading"><%= t(".heading") %></div>
8
+
9
+ <div class="application-nav__link-container">
10
+ <a class="application-nav__link application-nav__link--tasks<%= " application-nav__link--selected" if params["controller"] == "clockface/tasks" %> navbar-brand" href="<%= clockface.tasks_path %>">
11
+ <span><%= t(".links.tasks") %></span>
12
+ </a>
13
+ <a class="application-nav__link application-nav__link--events<%= " application-nav__link--selected" if params["controller"] == "clockface/events" %> navbar-brand" href="<%= clockface.events_path %>">
14
+ <span><%= t(".links.events") %></span>
15
+ </a>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ </nav>
@@ -0,0 +1,130 @@
1
+ <div class="events-new__form-container">
2
+ <%= form_for(event, url: form_url, html: { class: "form-horizontal" }) do |form| %>
3
+ <div class="events-form__form-element--name form-group">
4
+ <label class="control-label col-sm-2" for="name">
5
+ <%= Clockface::Event.human_attribute_name("name") %>
6
+ </label>
7
+ <div class="col-sm-10">
8
+ <% if allow_editing_task %>
9
+ <%=
10
+ form.select(
11
+ :clockface_task_id,
12
+ event_form_select_options_for_name,
13
+ { include_blank: false },
14
+ { class: "form-control" }
15
+ )
16
+ %>
17
+ <% else %>
18
+ <%= event.name %>
19
+ <% end %>
20
+ </div>
21
+ </div>
22
+
23
+ <div class="events-form__form-element--enabled form-group">
24
+ <label class="control-label col-sm-2" for="enabled">
25
+ <%= Clockface::Event.human_attribute_name("enabled") %>
26
+ </label>
27
+ <div class="col-sm-10">
28
+ <%= form.check_box :enabled, checked: event.persisted? ? event.enabled? : true %>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="events-form__form-element--period form-group">
33
+ <label class="control-label col-sm-2" for="period">
34
+ <%= Clockface::Event.human_attribute_name("period") %>
35
+ </label>
36
+ <div class="col-sm-10">
37
+ <%=
38
+ form.text_field(
39
+ :period_value,
40
+ size: 2,
41
+ class: "events-form__form-element--period-value form-control"
42
+ ) %>
43
+ <%=
44
+ form.select(
45
+ :period_units,
46
+ event_form_select_options_for_period_units,
47
+ { include_blank: false },
48
+ { class: "events-form__form-element--period-units form-control" }
49
+ )
50
+ %>
51
+ </div>
52
+ </div>
53
+
54
+ <div class="events-form__form-element--at form-group">
55
+ <label class="control-label col-sm-2" for="at">
56
+ <%= Clockface::Event.human_attribute_name("at") %>
57
+ </label>
58
+ <div class="col-sm-10">
59
+ <%=
60
+ form.select(
61
+ :day_of_week,
62
+ event_form_select_options_for_day_of_week,
63
+ { include_blank: true },
64
+ { class: "events-form__form-element--day-of-week form-control" }
65
+ )
66
+ %>
67
+ &nbsp;
68
+ &nbsp;
69
+ <%=
70
+ form.select(
71
+ :hour,
72
+ event_form_select_options_for_hour,
73
+ { include_blank: false },
74
+ { class: "events-form__form-element--hour form-control" }
75
+ )
76
+ %>
77
+ <b>:</b>
78
+ <%=
79
+ form.select(
80
+ :minute,
81
+ event_form_select_options_for_minute,
82
+ { include_blank: false },
83
+ { class: "events-form__form-element--minute form-control" }
84
+ )
85
+ %>
86
+ </div>
87
+ </div>
88
+
89
+ <div class="events-form__form-element--time_zone form-group">
90
+ <label class="control-label col-sm-2" for="time_zone">
91
+ <%= Clockface::Event.human_attribute_name("time_zone") %>
92
+ </label>
93
+ <div class="col-sm-10">
94
+ <%=
95
+ form.time_zone_select(
96
+ :time_zone,
97
+ nil,
98
+ { include_blank: false, default: clockface_time_zone },
99
+ { class: "form-control" }
100
+ )
101
+ %>
102
+ </div>
103
+ </div>
104
+
105
+ <div class="events-form__form-element--if_condition form-group">
106
+ <label class="control-label col-sm-2" for="if_condition">
107
+ <%= Clockface::Event.human_attribute_name("if_condition") %>
108
+ </label>
109
+ <div class="col-sm-10">
110
+ <%=
111
+ form.select(
112
+ :if_condition,
113
+ event_form_select_options_for_if_condition,
114
+ { include_blank: true },
115
+ { class: "form-control" }
116
+ )
117
+ %>
118
+ </div>
119
+ </div>
120
+
121
+ <div class="form-group">
122
+ <div class="events-new__form-submit col-sm-2">
123
+ <%= link_to clockface.events_path do %>
124
+ <button type="button" class="btn btn-default"><%= t(".cancel") %></button>
125
+ <% end %>
126
+ <%= form.submit(t(".submit"), class: "btn btn-success") %>
127
+ </div>
128
+ </div>
129
+ <% end %>
130
+ </div>
@@ -0,0 +1,124 @@
1
+ <div class="container">
2
+ <div class="events-edit__heading-banner">
3
+ <h1>
4
+ <%= link_to clockface.events_path, class: "events-edit__heading-banner-link" do %>
5
+ <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
6
+ <% end %>
7
+ <%= t(".heading").downcase %>
8
+ </h1>
9
+ </div>
10
+
11
+ <%= render "clockface/application/flash" %>
12
+
13
+ <div class="events-delete__warning alert alert-danger" role="alert">
14
+ <div class="glyphicon glyphicon-warning-sign"></div>
15
+ <%= t(".warning") %>
16
+ </div>
17
+
18
+ <div class="events-delete__event-detail container">
19
+ <div class="events-delete__event-detail-element events-delete__event-detail-element--id form-group">
20
+ <label class="control-label col-sm-2" for="id">
21
+ <%= Clockface::Event.human_attribute_name("id") %>
22
+ </label>
23
+
24
+ <div class="col-sm-10">
25
+ <%= @event.id %>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="events-delete__event-detail-element events-delete__event-detail-element--name form-group">
30
+ <label class="control-label col-sm-2" for="name">
31
+ <%= Clockface::Event.human_attribute_name("name") %>
32
+ </label>
33
+
34
+ <div class="col-sm-10">
35
+ <%= @event.name %>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="events-delete__event-detail-element events-delete__event-detail-element--period form-group">
40
+ <label class="control-label col-sm-2" for="period">
41
+ <%= Clockface::Event.human_attribute_name("period") %>
42
+ </label>
43
+
44
+ <div class="col-sm-10">
45
+ <%= @event.period %>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="events-delete__event-detail-element events-delete__event-detail-element--at form-group">
50
+ <label class="control-label col-sm-2" for="at">
51
+ <%= Clockface::Event.human_attribute_name("at") %>
52
+ </label>
53
+
54
+ <div class="col-sm-10">
55
+ <%= @event.at %>
56
+ </div>
57
+ </div>
58
+
59
+ <div class="events-delete__event-detail-element events-delete__event-detail-element--time_zone form-group">
60
+ <label class="control-label col-sm-2" for="time_zone">
61
+ <%= Clockface::Event.human_attribute_name("time_zone") %>
62
+ </label>
63
+
64
+ <div class="col-sm-10">
65
+ <%= @event.time_zone %>
66
+ </div>
67
+ </div>
68
+
69
+ <div class="events-delete__event-detail-element events-delete__event-detail-element--if_condition form-group">
70
+ <label class="control-label col-sm-2" for="if_condition">
71
+ <%= Clockface::Event.human_attribute_name("if_condition") %>
72
+ </label>
73
+
74
+ <div class="col-sm-10">
75
+ <%= @event.if_condition %>
76
+ </div>
77
+ </div>
78
+
79
+ <div class="events-delete__event-detail-element events-delete__event-detail-element--last_triggered_at form-group">
80
+ <label class="control-label col-sm-2" for="last_triggered_at">
81
+ <%= Clockface::Event.human_attribute_name("last_triggered_at") %>
82
+ </label>
83
+
84
+ <div class="col-sm-10">
85
+ <%= raw @event.last_triggered_at %>
86
+ </div>
87
+ </div>
88
+
89
+ <div class="events-delete__event-detail-element events-delete__event-detail-element--enabled form-group">
90
+ <label class="control-label col-sm-2" for="enabled">
91
+ <%= Clockface::Event.human_attribute_name("enabled") %>
92
+ </label>
93
+
94
+ <div class="col-sm-10">
95
+ <span class="<%= @event.enabled? ? "enabled-event" : "disabled-event" %> glyphicon glyphicon-<%= @event.enabled? ? "ok" : "remove" %>" aria-hidden="true">
96
+ </span>
97
+ </div>
98
+ </div>
99
+ </div>
100
+
101
+ <div class="events-delete__captcha-label">
102
+ <%= raw t(".captcha_label", captcha: @captcha) %>
103
+ </div>
104
+
105
+ <%= form_for(@event, url: clockface.event_path(@event), method: "DELETE", html: { class: "form-horizontal" }) do |form| %>
106
+ <%=
107
+ text_field_tag(
108
+ :captcha,
109
+ nil,
110
+ size: Clockface::EventsController::CAPTCHA_LENGTH,
111
+ class: "events-delete__form-element--captcha form-control"
112
+ )
113
+ %>
114
+
115
+ <div class="form-group">
116
+ <div class="events-delete__form-submit col-sm-2">
117
+ <%= link_to clockface.events_path do %>
118
+ <button type="button" class="btn btn-default"><%= t(".cancel") %></button>
119
+ <% end %>
120
+ <%= submit_tag(t(".submit"), class: "btn btn-danger") %>
121
+ </div>
122
+ </div>
123
+ <% end %>
124
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="container">
2
+ <div class="events-edit__heading-banner">
3
+ <h1>
4
+ <%= link_to clockface.events_path, class: "events-edit__heading-banner-link" do %>
5
+ <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
6
+ <% end %>
7
+ <%= t(".heading").downcase %>
8
+ </h1>
9
+ </div>
10
+
11
+ <%= render "clockface/application/flash" %>
12
+
13
+ <%= render "event_form", event: @event, form_url: clockface.event_path(@event), allow_editing_task: false %>
14
+ </div>