foreman_openscap 0.5.0 → 0.5.1

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_openscap/arf_reports.js +16 -0
  3. data/app/assets/javascripts/foreman_openscap/openscap_proxy.js +52 -0
  4. data/app/assets/javascripts/foreman_openscap/reports.js +6 -0
  5. data/app/assets/stylesheets/foreman_openscap/policy.css +2 -1
  6. data/app/assets/stylesheets/foreman_openscap/reports.css +3 -0
  7. data/app/controllers/api/v2/compliance/arf_reports_controller.rb +2 -2
  8. data/app/controllers/api/v2/compliance/policies_controller.rb +6 -6
  9. data/app/controllers/api/v2/compliance/scap_contents_controller.rb +2 -2
  10. data/app/controllers/arf_reports_controller.rb +41 -5
  11. data/app/controllers/concerns/foreman_openscap/hostgroups_controller_extensions.rb +20 -0
  12. data/app/controllers/concerns/foreman_openscap/hosts_common_controller_extensions.rb +45 -0
  13. data/app/controllers/concerns/foreman_openscap/hosts_controller_extensions.rb +17 -0
  14. data/app/controllers/policies_controller.rb +4 -4
  15. data/app/helpers/arf_report_dashboard_helper.rb +5 -5
  16. data/app/helpers/arf_reports_helper.rb +28 -0
  17. data/app/helpers/compliance_hosts_helper.rb +5 -3
  18. data/app/helpers/policies_helper.rb +3 -3
  19. data/app/helpers/policy_dashboard_helper.rb +2 -2
  20. data/app/lib/proxy_api/available_proxy.rb +7 -7
  21. data/app/lib/proxy_api/openscap.rb +10 -0
  22. data/app/mailers/foreman_openscap/policy_mailer.rb +2 -2
  23. data/app/models/concerns/foreman_openscap/compliance_status_scoped_search.rb +5 -5
  24. data/app/models/concerns/foreman_openscap/host_extensions.rb +22 -14
  25. data/app/models/concerns/foreman_openscap/hostgroup_extensions.rb +12 -0
  26. data/app/models/concerns/foreman_openscap/log_extensions.rb +8 -0
  27. data/app/models/concerns/foreman_openscap/openscap_proxy_core_extensions.rb +55 -0
  28. data/app/models/concerns/foreman_openscap/openscap_proxy_extensions.rb +17 -0
  29. data/app/models/concerns/foreman_openscap/smart_proxy_extensions.rb +23 -0
  30. data/app/models/foreman_openscap/arf_report.rb +42 -21
  31. data/app/models/foreman_openscap/policy.rb +38 -38
  32. data/app/models/foreman_openscap/scap_content.rb +9 -9
  33. data/app/overrides/hostgroups/form/select_openscap_proxy.rb +4 -0
  34. data/app/overrides/hosts/form/select_openscap_proxy.rb +4 -0
  35. data/app/services/foreman_openscap/host_report_dashboard/data.rb +5 -5
  36. data/app/services/foreman_openscap/report_dashboard/data.rb +5 -5
  37. data/app/views/arf_reports/_detailed_message.html.erb +9 -0
  38. data/app/views/arf_reports/_list.html.erb +29 -9
  39. data/app/views/arf_reports/_output.html.erb +12 -8
  40. data/app/views/arf_reports/delete_multiple.html.erb +29 -0
  41. data/app/views/arf_reports/index.html.erb +1 -1
  42. data/app/views/arf_reports/show.html.erb +3 -1
  43. data/app/views/compliance_hosts/_openscap_proxy.html.erb +8 -0
  44. data/app/views/policies/_form.html.erb +1 -1
  45. data/app/views/policies/steps/_schedule_form.html.erb +1 -1
  46. data/config/routes.rb +17 -1
  47. data/db/migrate/20141013172051_create_scaptimony_policies.rb +1 -1
  48. data/db/migrate/20141014105333_create_scaptimony_assets.rb +2 -2
  49. data/db/migrate/20141015092642_create_scaptimony_arf_reports.rb +4 -4
  50. data/db/migrate/20141104164201_create_scaptimony_scap_contents.rb +1 -3
  51. data/db/migrate/20141104171545_create_scaptimony_policy_revisions.rb +2 -2
  52. data/db/migrate/20141105174834_add_columns_to_scaptimony_policies.rb +1 -1
  53. data/db/migrate/20141113221054_create_scaptimony_scap_content_profiles.rb +1 -1
  54. data/db/migrate/20141116171305_add_profile_to_scaptimony_policies.rb +1 -1
  55. data/db/migrate/20141119182606_create_scaptimony_xccdf_rule_results.rb +3 -3
  56. data/db/migrate/20150821100137_migrate_from_scaptimony.rb +2 -2
  57. data/db/migrate/20150929152345_move_arf_reports_to_reports_table.rb +1 -1
  58. data/db/migrate/20151023131950_link_arf_report_directly_to_host.rb +12 -8
  59. data/db/migrate/20151118165125_add_size_to_scap_content.rb +5 -0
  60. data/db/migrate/20151119155419_add_arf_fields_to_message.rb +8 -0
  61. data/db/migrate/20151120090851_add_openscap_proxy_to_host_and_hostgroup.rb +25 -0
  62. data/db/seeds.d/openscap_feature.rb +1 -1
  63. data/db/seeds.d/openscap_scap_default.rb +1 -1
  64. data/lib/foreman_openscap/bulk_upload.rb +11 -12
  65. data/lib/foreman_openscap/engine.rb +22 -11
  66. data/lib/foreman_openscap/helper.rb +1 -1
  67. data/lib/foreman_openscap/version.rb +1 -1
  68. data/lib/tasks/foreman_openscap_tasks.rake +3 -2
  69. data/test/factories/arf_report_factory.rb +2 -1
  70. data/test/factories/compliance_host_factory.rb +1 -0
  71. data/test/factories/scap_content_related.rb +1 -1
  72. data/test/functional/api/v2/compliance/arf_reports_controller_test.rb +3 -3
  73. data/test/functional/api/v2/compliance/policies_controller_test.rb +1 -1
  74. data/test/functional/api/v2/compliance/scap_contents_controller_test.rb +2 -2
  75. data/test/functional/arf_reports_controller_test.rb +21 -0
  76. data/test/lib/foreman_openscap/bulk_upload_test.rb +2 -2
  77. data/test/test_plugin_helper.rb +2 -2
  78. data/test/unit/arf_report_test.rb +12 -1
  79. data/test/unit/compliance_status_test.rb +6 -5
  80. data/test/unit/openscap_host_test.rb +4 -0
  81. data/test/unit/policy_mailer_test.rb +1 -1
  82. data/test/unit/scap_content_test.rb +1 -1
  83. metadata +29 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4717f3191e1244335417139e2dddef5297b612d
4
- data.tar.gz: 54ac0f517e808a186d74a6a42a03f07a79565abf
3
+ metadata.gz: 121b364917afa0cdfdc713d1faf79d3ab6ccd3b9
4
+ data.tar.gz: 28b664428cc89f6b6f5f668d94b4d9df84b73ea0
5
5
  SHA512:
6
- metadata.gz: 2dbb8f627fdb059e1099c4f95288bfdc7b4ee9e2eef011cec3c45c9c7419ee8ccf7e99813894d46ba659c8d2cfc88d44cc86ebec0a5e24f91312489478133193
7
- data.tar.gz: c74cd26daf940251810f9aae28728897da0d8d35848939476c2e90a882ae569e5a698835c11b24d669539c187c2bd6526a3c46f6e319bf5b251382190c09ae11
6
+ metadata.gz: d5fe4ad1b975934842292b33f3ccfb147d69054cdfb0c4d65c73c31cd905ebb863ba11cdab59d3b7f5a6dbd2fcbfd3e575ec9d5257d9ce8eccf5c0ffe205d2fb
7
+ data.tar.gz: fad85cd933a8799fd6bb064f816c4770531ccd97e72c2d0e052ddeb63c8f45972adf087f0dc193e90eae6f894fad582043cf31f203b3f4f2d1ef40e4a83ab2f7
@@ -0,0 +1,16 @@
1
+ function buildArfModal(element, url) {
2
+ var url = url + "?" + $.param({arf_report_ids: $.foremanSelectedHosts});
3
+ var title = $(element).attr('data-dialog-title');
4
+ $('#confirmation-modal .modal-header h4').text(title);
5
+ $("#confirmation-modal .modal-body").load(url + " #content",
6
+ function(response, status, xhr) {
7
+ $('#submit_multiple').val('');
8
+ var b = $("#confirmation-modal .btn-primary");
9
+ if ($(response).find('#content form select').length > 0)
10
+ b.addClass("disabled").attr("disabled", true);
11
+ else
12
+ b.removeClass("disabled").attr("disabled", false);
13
+ $('#confirmation-modal').modal();
14
+ });
15
+ return false;
16
+ }
@@ -0,0 +1,52 @@
1
+ function updateOpenscapProxy(element){
2
+ var id = $("form").data('id')
3
+ var url = $(element).attr('data-url');
4
+ var data = $("form").serialize().replace('method=put', 'method=post');
5
+ if (url.match('hostgroups')) {
6
+ data = data + '&hostgroup_id=' + id
7
+ } else {
8
+ data = data + '&host_id=' + id
9
+ }
10
+
11
+ toggleErrorText("");
12
+ $(element).indicator_show();
13
+ $.ajax({
14
+ type: 'post',
15
+ url: url,
16
+ data: data,
17
+ error: function(response) {
18
+ var text = $(response.responseText).find('.form-group.has-error').find('.help-block.help-inline').text();
19
+ addOpenscapProxyError(text);
20
+ $(element).indicator_hide();
21
+ },
22
+ success: function(response) {
23
+ removeOpenscapProxyError();
24
+ $('#puppetclasses_parameters').replaceWith($(response).find("#puppetclasses_parameters"));
25
+ $(element).indicator_hide();
26
+ }
27
+ })
28
+ }
29
+
30
+ function findOpenscapProxyFormGroup(){
31
+ return $('form').find("label[for='openscap_proxy_id']").parents(".form-group").first();
32
+ }
33
+
34
+ function addOpenscapProxyError(text){
35
+ var formGroup = findOpenscapProxyFormGroup();
36
+ $(formGroup).addClass("has-error");
37
+ toggleErrorText(text);
38
+ }
39
+
40
+ function removeOpenscapProxyError(){
41
+ var formGroup = findOpenscapProxyFormGroup();
42
+ $(formGroup).removeClass("has-error");
43
+ toggleErrorText("");
44
+ }
45
+
46
+ function toggleErrorText(text){
47
+ if(text){
48
+ $(findOpenscapProxyFormGroup()).find(".help-block.help-inline").append('<span id="openscap_error">' + text + '</span>');
49
+ } else {
50
+ $(findOpenscapProxyFormGroup()).find("#openscap_error").remove();
51
+ }
52
+ }
@@ -0,0 +1,6 @@
1
+ function showReportDetails(log_id, event) {
2
+ var showDetails = $('#details-' + log_id)
3
+ showDetails.toggle();
4
+ showDetails.is(':visible') ? $(event).find('span').attr('class', 'glyphicon glyphicon-collapse-up') : $(event).find('span').attr('class', 'glyphicon glyphicon-collapse-down');
5
+ }
6
+
@@ -4,4 +4,5 @@
4
4
 
5
5
  .hide-pane {
6
6
  display: none;
7
- }
7
+ }
8
+
@@ -0,0 +1,3 @@
1
+ #report_log a.more-info {
2
+ color: #CAC5C5;
3
+ }
@@ -20,14 +20,14 @@ module Api
20
20
 
21
21
  add_smart_proxy_filters :create, :features => 'Openscap'
22
22
 
23
- before_filter :find_resource, :only => %w{show destroy}
23
+ before_filter :find_resource, :only => %w(show destroy)
24
24
 
25
25
  def resource_name
26
26
  '::ForemanOpenscap::ArfReport'
27
27
  end
28
28
 
29
29
  def get_resource
30
- instance_variable_get :"@arf_report" or raise 'no resource loaded'
30
+ instance_variable_get :"@arf_report" or fail 'no resource loaded'
31
31
  end
32
32
 
33
33
  resource_description do
@@ -5,7 +5,7 @@ module Api::V2
5
5
 
6
6
  add_smart_proxy_filters :content, :features => 'Openscap'
7
7
 
8
- before_filter :find_resource, :except => %w{index create}
8
+ before_filter :find_resource, :except => %w(index create)
9
9
 
10
10
  skip_after_filter :log_response_body, :only => [:content]
11
11
 
@@ -14,7 +14,7 @@ module Api::V2
14
14
  end
15
15
 
16
16
  def get_resource
17
- instance_variable_get :"@policy" or raise 'no resource loaded'
17
+ instance_variable_get :"@policy" or fail 'no resource loaded'
18
18
  end
19
19
 
20
20
  def policy_url(policy = nil)
@@ -94,10 +94,10 @@ module Api::V2
94
94
 
95
95
  def action_permission
96
96
  case params[:action]
97
- when 'content'
98
- :view
99
- else
100
- super
97
+ when 'content'
98
+ :view
99
+ else
100
+ super
101
101
  end
102
102
  end
103
103
  end
@@ -1,14 +1,14 @@
1
1
  module Api::V2
2
2
  module Compliance
3
3
  class ScapContentsController < ::Api::V2::BaseController
4
- before_filter :find_resource, :except => %w{index create}
4
+ before_filter :find_resource, :except => %w(index create)
5
5
 
6
6
  def resource_name
7
7
  '::ForemanOpenscap::ScapContent'
8
8
  end
9
9
 
10
10
  def get_resource
11
- instance_variable_get :"@scap_content" or raise 'no resource loaded'
11
+ instance_variable_get :"@scap_content" or fail 'no resource loaded'
12
12
  end
13
13
 
14
14
  resource_description do
@@ -1,14 +1,15 @@
1
1
  class ArfReportsController < ApplicationController
2
2
  include Foreman::Controller::AutoCompleteSearch
3
3
 
4
- before_filter :find_by_id, :only => [:show, :show_html, :destroy, :parse_html, :parse_bzip]
4
+ before_filter :find_arf_report, :only => [:show, :show_html, :destroy, :parse_html, :parse_bzip]
5
+ before_filter :find_multiple, :only => [:delete_multiple, :submit_delete_multiple]
5
6
 
6
7
  def model_of_controller
7
8
  ::ForemanOpenscap::ArfReport
8
9
  end
9
10
 
10
11
  def index
11
- @arf_reports = resource_base.includes(:asset)
12
+ @arf_reports = resource_base.includes(:host => [:policies, :last_report_object, :host_statuses])
12
13
  .search_for(params[:search], :order => params[:order])
13
14
  .paginate(:page => params[:page], :per_page => params[:per_page])
14
15
  end
@@ -41,20 +42,55 @@ class ArfReportsController < ApplicationController
41
42
  if @arf_report.destroy
42
43
  process_success(:success_msg => (_("Successfully deleted Arf report.")), :success_redirect => arf_reports_path)
43
44
  else
44
- process_error
45
+ process_error(:error_msg => _("Failed to delete Arf Report for host #{@arf_report.host.name} reported at #{@arf_report.reported_at}"))
46
+ end
47
+ end
48
+
49
+ def delete_multiple
50
+ end
51
+
52
+ def submit_delete_multiple
53
+ failed_deletes = @arf_reports.reject(&:destroy).count
54
+ if failed_deletes > 0
55
+ process_error(:error_msg => (_("Failed to delete %s compliance reports") % failed_deletes),
56
+ :error_redirect => arf_reports_path)
57
+ else
58
+ process_success(:success_msg => (_("Successfully deleted %s compliance reports") % @arf_reports.count),
59
+ :success_redirect => arf_reports_path)
45
60
  end
46
61
  end
47
62
 
48
63
  private
49
64
 
50
- def find_by_id
51
- @arf_report = resource_base.find(params[:id])
65
+ def find_arf_report
66
+ @arf_report = resource_base.includes(:logs => [:message, :source]).find(params[:id])
67
+ end
68
+
69
+ def find_multiple
70
+ if params[:arf_report_ids].present?
71
+ @arf_reports = ::ForemanOpenscap::ArfReport.where(:id => params[:arf_report_ids])
72
+ if @arf_reports.empty?
73
+ error _('No complince reports were found.')
74
+ redirect_to(arf_reports_path) and return false
75
+ end
76
+ else
77
+ error _('No complince reports selected')
78
+ redirect_to(arf_reports_path) and return false
79
+ end
80
+ return @arf_reports
81
+ rescue => e
82
+ error _("Something went wrong while selecting compliance reports - %s") % (e)
83
+ logger.debug e.message
84
+ logger.debug e.backtrace.join("\n")
85
+ redirect_to arf_reports_path and return false
52
86
  end
53
87
 
54
88
  def action_permission
55
89
  case params[:action]
56
90
  when 'show_html', 'parse_html', 'parse_bzip'
57
91
  :view
92
+ when 'delete_multiple', 'submit_delete_multiple'
93
+ :destroy
58
94
  else
59
95
  super
60
96
  end
@@ -0,0 +1,20 @@
1
+ module ForemanOpenscap
2
+ module HostgroupsControllerExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ alias_method_chain :load_vars_for_ajax, :openscap
7
+ alias_method_chain :inherit_parent_attributes, :openscap
8
+ end
9
+
10
+ def load_vars_for_ajax_with_openscap
11
+ load_vars_for_ajax_without_openscap
12
+ @openscap_proxy = @hostgroup.openscap_proxy
13
+ end
14
+
15
+ def inherit_parent_attributes_with_openscap
16
+ inherit_parent_attributes_without_openscap
17
+ @hostgroup.openscap_proxy ||= @parent.openscap_proxy
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ module ForemanOpenscap
2
+ module HostsCommonControllerExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ def openscap_proxy_changed
6
+ model = if params[param_id_key].blank?
7
+ controller_name.classify.constantize.new(params[param_model_key])
8
+ else
9
+ resource_base.find(params[param_id_key])
10
+ end
11
+ proxy_id = params[param_model_key][:openscap_proxy_id]
12
+ instance_variable_set("@#{param_model_key}", model)
13
+ if proxy_id.blank?
14
+ render :partial => "form"
15
+ else
16
+ begin
17
+ instance_variable_get("@#{param_model_key}").update_scap_client_params(proxy_id)
18
+ render :partial => "form"
19
+ rescue => e
20
+ instance_variable_get("@#{param_model_key}").errors.add(:openscap_proxy_id, e.message)
21
+ render :partial => "form", :status => 422
22
+ end
23
+ end
24
+ end
25
+
26
+ def action_permission
27
+ case params[:action]
28
+ when 'openscap_proxy_changed'
29
+ :edit
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def param_id_key
38
+ "#{param_model_key}_id"
39
+ end
40
+
41
+ def param_model_key
42
+ controller_name.singularize
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ module ForemanOpenscap
2
+ module HostsControllerExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ alias_method_chain :process_hostgroup, :openscap
7
+ self::AJAX_REQUESTS << 'openscap_proxy_changed'
8
+ end
9
+
10
+ def process_hostgroup_with_openscap
11
+ @hostgroup = Hostgroup.find(params[:host][:hostgroup_id]) if params[:host][:hostgroup_id].to_i > 0
12
+ return head(:not_found) unless @hostgroup
13
+ @openscap_proxy = @hostgroup.openscap_proxy
14
+ process_hostgroup_without_openscap
15
+ end
16
+ end
17
+ end
@@ -134,10 +134,10 @@ class PoliciesController < ApplicationController
134
134
 
135
135
  def action_permission
136
136
  case params[:action]
137
- when 'parse'
138
- :view
139
- else
140
- super
137
+ when 'parse'
138
+ :view
139
+ else
140
+ super
141
141
  end
142
142
  end
143
143
  end
@@ -1,8 +1,8 @@
1
1
  module ArfReportDashboardHelper
2
2
  COLORS = {
3
- :passed => '#89A54E',
4
- :failed => '#AA4643',
5
- :othered => '#DB843D',
3
+ :passed => '#89A54E',
4
+ :failed => '#AA4643',
5
+ :othered => '#DB843D',
6
6
  }
7
7
 
8
8
  def reports_breakdown_chart(report, options = {})
@@ -10,9 +10,9 @@ module ArfReportDashboardHelper
10
10
  [[:failed, _('Failed')],
11
11
  [:passed, _('Passed')],
12
12
  [:othered, _('Othered')],
13
- ].each { |i|
13
+ ].each do |i|
14
14
  data << {:label => i[1], :data => report[i[0]], :color => COLORS[i[0]]}
15
- }
15
+ end
16
16
  flot_pie_chart 'overview', _('Compliance reports breakdown'), data, options
17
17
  end
18
18
  end
@@ -34,4 +34,32 @@ module ArfReportsHelper
34
34
  end
35
35
  "class='label label-#{tag}'".html_safe
36
36
  end
37
+
38
+ def severity_tag(level)
39
+ tag = case level.downcase.to_sym
40
+ when :low
41
+ "info"
42
+ when :medium
43
+ "warning"
44
+ when :high
45
+ "danger"
46
+ else
47
+ "default"
48
+ end
49
+ "class='label label-#{tag}'".html_safe
50
+ end
51
+
52
+ def multiple_actions_arf_report
53
+ actions = [
54
+ [_('Delete reports'), delete_multiple_arf_reports_path]
55
+ ]
56
+ end
57
+
58
+ def multiple_actions_arf_report_select
59
+ select_action_button(_("Select Action"), {:id => 'submit_multiple'},
60
+ multiple_actions_arf_report.map do |action|
61
+ link_to_function(action[0], "buildArfModal(this, '#{action[1]}')",
62
+ :'data-dialog-title' => _("%s - The following compliance reports are about to be changed") % action[0])
63
+ end.flatten)
64
+ end
37
65
  end
@@ -5,14 +5,16 @@ module ComplianceHostsHelper
5
5
  [[:passed, _('Passed')],
6
6
  [:failed, _('Failed')],
7
7
  [:othered, _('Other')],
8
- ].each { |i|
8
+ ].each do |i|
9
9
  data << {:label => i[1], :data => report[i[0]], :color => ArfReportDashboardHelper::COLORS[i[0]]}
10
- }
10
+ end
11
11
  flot_pie_chart 'overview', _('Compliance reports breakdown'), data, options
12
12
  end
13
13
 
14
14
  def host_arf_reports_chart(policy_id)
15
- passed, failed, othered, = [], [], []
15
+ passed = []
16
+ failed = []
17
+ othered = []
16
18
  @host.arf_reports.of_policy(policy_id).each do |report|
17
19
  passed << [report.created_at.to_i*1000, report.passed]
18
20
  failed << [report.created_at.to_i*1000, report.failed]
@@ -9,7 +9,7 @@ module PoliciesHelper
9
9
  scap_contents = ::ForemanOpenscap::ScapContent.all
10
10
  if scap_contents.length > 1
11
11
  select_f form, :scap_content_id, scap_contents, :id, :title,
12
- {:include_blank => _("Choose existing SCAP Content")} ,
12
+ {:include_blank => _("Choose existing SCAP Content")},
13
13
  {:label => _("SCAP Content"),
14
14
  :onchange => 'scap_content_selected(this);',
15
15
  :'data-url' => method_path('scap_content_selected')}
@@ -44,7 +44,7 @@ module PoliciesHelper
44
44
  content_tag(:div, :class => "form-actions") do
45
45
  text = overwrite ? overwrite : _("Submit")
46
46
  options = {:class => "btn btn-primary"}
47
- options.merge! :'data-id' => form_to_submit_id(form) unless options.has_key?(:'data-id')
47
+ options.merge! :'data-id' => form_to_submit_id(form) unless options.key?(:'data-id')
48
48
  previous = form.object.first_step? ? ' ' : previous_link(form)
49
49
  cancel_and_submit = content_tag(:div, :class => "pull-right") do
50
50
  link_to(_("Cancel"), args[:cancel_path], :class => "btn btn-default") + ' ' +
@@ -67,6 +67,6 @@ module PoliciesHelper
67
67
  end
68
68
 
69
69
  def days_of_week_hash
70
- Hash[*Date::DAYNAMES.map{ |day| [day.downcase, day]}.flatten]
70
+ Hash[*Date::DAYNAMES.map { |day| [day.downcase, day] }.flatten]
71
71
  end
72
72
  end