rocketjob_mission_control 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/rocket_job_mission_control/dirmon_entries.js.coffee +18 -0
  3. data/app/assets/stylesheets/rocket_job_mission_control/base.scss +33 -59
  4. data/app/assets/stylesheets/rocket_job_mission_control/bootstrap_and_overrides.scss +12 -0
  5. data/app/assets/stylesheets/rocket_job_mission_control/callout.scss +8 -0
  6. data/app/assets/stylesheets/rocket_job_mission_control/jobs.scss +35 -0
  7. data/app/controllers/rocket_job_mission_control/application_controller.rb +0 -2
  8. data/app/controllers/rocket_job_mission_control/dirmon_entries_controller.rb +125 -0
  9. data/app/controllers/rocket_job_mission_control/jobs/failures_controller.rb +31 -0
  10. data/app/helpers/rocket_job_mission_control/application_helper.rb +7 -0
  11. data/app/helpers/rocket_job_mission_control/jobs_helper.rb +18 -11
  12. data/app/helpers/rocket_job_mission_control/pagination_helper.rb +7 -0
  13. data/app/helpers/rocket_job_mission_control/slices_helper.rb +9 -0
  14. data/app/models/job_failures.rb +30 -0
  15. data/app/views/layouts/rocket_job_mission_control/application.html.haml +3 -1
  16. data/app/views/layouts/rocket_job_mission_control/partials/_header.html.haml +2 -2
  17. data/app/views/layouts/rocket_job_mission_control/partials/_sidebar.html.haml +4 -0
  18. data/app/views/rocket_job_mission_control/dirmon_entries/_form.html.haml +36 -0
  19. data/app/views/rocket_job_mission_control/dirmon_entries/_list.html.haml +31 -0
  20. data/app/views/rocket_job_mission_control/dirmon_entries/_properties.html.haml +8 -0
  21. data/app/views/rocket_job_mission_control/dirmon_entries/_status.html.haml +23 -0
  22. data/app/views/rocket_job_mission_control/dirmon_entries/edit.html.haml +8 -0
  23. data/app/views/rocket_job_mission_control/dirmon_entries/index.html.haml +11 -0
  24. data/app/views/rocket_job_mission_control/dirmon_entries/new.html.haml +9 -0
  25. data/app/views/rocket_job_mission_control/dirmon_entries/show.html.haml +26 -0
  26. data/app/views/rocket_job_mission_control/jobs/_list.html.haml +34 -30
  27. data/app/views/rocket_job_mission_control/jobs/failures/_pagination.html.haml +16 -0
  28. data/app/views/rocket_job_mission_control/jobs/failures/index.html.haml +32 -0
  29. data/app/views/rocket_job_mission_control/jobs/index.html.haml +10 -8
  30. data/app/views/rocket_job_mission_control/jobs/running.html.haml +1 -1
  31. data/app/views/rocket_job_mission_control/jobs/show.html.haml +39 -35
  32. data/app/views/rocket_job_mission_control/workers/_actions.html.haml +4 -4
  33. data/app/views/rocket_job_mission_control/workers/index.html.haml +50 -45
  34. data/config/locales/en.yml +23 -1
  35. data/config/routes.rb +8 -0
  36. data/lib/rocket_job_mission_control/version.rb +1 -1
  37. data/spec/controllers/dirmon_entries_controller_spec.rb +451 -0
  38. data/spec/controllers/jobs/failures_controller_spec.rb +60 -0
  39. data/spec/dummy/config/environments/test.rb +1 -1
  40. data/spec/dummy/config/mongo.yml +15 -0
  41. data/spec/dummy/log/development.log +2 -0
  42. data/spec/dummy/log/test.log +52763 -2015
  43. data/spec/helpers/jobs_helper_spec.rb +27 -0
  44. data/spec/helpers/pagination_helper_spec.rb +21 -0
  45. data/spec/helpers/slices_helper_spec.rb +33 -0
  46. data/spec/models/job_failures_spec.rb +14 -0
  47. data/spec/views/workers/index.html.haml_spec.rb +23 -0
  48. metadata +35 -4
@@ -0,0 +1,23 @@
1
+ %div
2
+ %label Name:
3
+ = @dirmon_entry.name
4
+ %div
5
+ %label Pattern:
6
+ = @dirmon_entry.pattern
7
+ %div
8
+ %label Job Class Name:
9
+ = @dirmon_entry.job_class_name
10
+ %div
11
+ %label Archive Directory:
12
+ = @dirmon_entry.archive_directory
13
+ .parameters
14
+ %label Arguments:
15
+ %pre
16
+ %code= pretty_print_array_or_hash(@dirmon_entry.arguments)
17
+ .parameters
18
+ %label Properties:
19
+ %pre
20
+ %code= pretty_print_array_or_hash(@dirmon_entry.properties)
21
+ .id
22
+ %label ID:
23
+ = @dirmon_entry.id
@@ -0,0 +1,8 @@
1
+ .row
2
+ #queue
3
+ = render partial: 'list', locals: { jobs: @jobs }
4
+
5
+ .col-md-6
6
+ .lead Edit Dirmon Entry
7
+
8
+ = render partial: 'form', locals: { action: :update }
@@ -0,0 +1,11 @@
1
+ .row
2
+ #queue
3
+ = render partial: 'list', locals: { jobs: @jobs }
4
+
5
+ .col-md-8
6
+ .job-status
7
+ #job
8
+ .welcome_screen
9
+ = link_to image_tag('rocket_job_mission_control/rocket-icon-512x512.png'), 'http://rocketjob.io'
10
+ %h1 Welcome to rocketjob
11
+ %p High volume, priority based, Enterprise Batch Processing solution for Ruby
@@ -0,0 +1,9 @@
1
+ .row
2
+ #queue
3
+
4
+ = render partial: 'list', locals: { jobs: @jobs }
5
+
6
+ .col-md-6
7
+ .lead New Dirmon Entry
8
+
9
+ = render partial: 'form'
@@ -0,0 +1,26 @@
1
+ .row
2
+ #queue
3
+ = render partial: 'list', locals: { jobs: @jobs }
4
+
5
+ .col-md-8.job-status
6
+ #job
7
+ .lead= @dirmon_entry.job_class_name + "#{'#' + @dirmon_entry.perform_method.to_s unless @dirmon_entry.perform_method == :perform }"
8
+
9
+ .job-state.inline-block
10
+ .left status
11
+ .right{ class: @dirmon_entry.state }
12
+ = @dirmon_entry.state
13
+
14
+ .btn-toolbar.job-actions.pull-right
15
+ .btn-group{ style: 'margin-right: 1em;' }
16
+ = link_to "Destroy", dirmon_entry_path(@dirmon_entry), method: :delete, class: 'btn btn-default', :data => { :confirm => 'Are you sure?' }
17
+ - if @dirmon_entry.disabled? || @dirmon_entry.pending?
18
+ = link_to "Enable", enable_dirmon_entry_path(@dirmon_entry.id), method: :put, class: "btn btn-default"
19
+ - if @dirmon_entry.enabled?
20
+ = link_to "Disable", disable_dirmon_entry_path(@dirmon_entry.id), method: :put, class: "btn btn-default", :data => { :confirm => 'Are you sure?' }
21
+ .btn-group
22
+ = link_to "Edit", edit_dirmon_entry_path(@dirmon_entry), class: "btn btn-default"
23
+
24
+ .clearfix
25
+
26
+ = render partial: 'status', locals: { dirmon_entry: @job }
@@ -1,31 +1,35 @@
1
- .col-md-4.job-list
2
- .filter.btn-group.btn-group-justified{ style: 'padding: 1px 1px 1px 1px;', data: { toggle: 'buttons' } }
3
- - job_states.each do |state|
4
- - enabled = @states.include?(state.to_s) || @states.empty?
5
- - title = enabled ? 'Hide' : 'Show'
6
- %label.btn.btn-default.state-toggle{ class: ('active' if enabled), title: "#{title} #{state.capitalize}" }
7
- = check_box_tag(state, state, enabled)
8
- %i.fa{class: job_state_icon(state)}
9
- .clearfix
10
- .list
11
- - if jobs.empty?
12
- .card.inner
13
- .title
14
- .lead.text-center No Jobs Found.
15
- - else
16
- - jobs.each do |job|
17
- = link_to job_path(id: job.id, states: @states), class: "card callout callout-#{job.state} #{job_selected_class(job, @job)}" do
18
- .inner
19
- .title
20
- .lead
21
- %i.fa{class: job_icon(job), style: 'font-size: 75%;', title: job.state}
22
- = job_title(job)
1
+ .col-md-4
2
+ .job-list
3
+ .filter
4
+ %ol.breadcrumb
5
+ %li.active Jobs
6
+ .btn-group.pull-right{ data: { toggle: 'buttons' } }
7
+ - job_states.each do |state|
8
+ - enabled = @states.include?(state.to_s) || @states.empty?
9
+ - title = enabled ? 'Hide' : 'Show'
10
+ %label.btn.btn-default.state-toggle{ class: ('active' if enabled), title: "#{title} #{state.capitalize}" }
11
+ = check_box_tag(state, state, enabled)
12
+ %i.fa{class: job_state_icon(state)}
13
+ .clearfix
14
+ .list
15
+ - if jobs.empty?
16
+ .card.inner
17
+ .title
18
+ .lead.text-center No Jobs Found.
19
+ - else
20
+ - jobs.each do |job|
21
+ = link_to(job_path(id: job.id, states: @states), class: "card callout callout-#{job.state} #{job_selected_class(job, @job)}") do
22
+ .inner
23
+ .title
24
+ .lead
25
+ %i.fa{class: job_icon(job), style: 'font-size: 75%;', title: job.state}
26
+ = job_title(job)
23
27
 
24
- .description= job.description.try(:truncate, 50)
25
- .info
26
- .duration.pull-left
27
- %i.fa.fa-clock-o
28
- = job.duration
29
- - if job.running?
30
- .progress.pull-right
31
- .progress-bar{ style: "width: #{job.percent_complete}%;", title: "#{job.percent_complete}% percent complete."}
28
+ .description= job.description.try(:truncate, 50)
29
+ .info
30
+ .duration.pull-left
31
+ %i.fa.fa-clock-o
32
+ = job.duration
33
+ - if job.running?
34
+ .progress.pull-right
35
+ .progress-bar{ style: "width: #{job.percent_complete}%;", title: "#{job.percent_complete}% percent complete."}
@@ -0,0 +1,16 @@
1
+ - previous_offset = [0, pagination[:offset].pred].max
2
+ - next_offset = [pagination[:total], pagination[:offset].next].min
3
+
4
+ %ul.pagination
5
+ %li
6
+ = link_to(job_failures_path(error_type: error_type, offset: 0), class: "btn btn-default #{page_nav_disabled_class(pagination[:offset], 0)}", title: 'First') do
7
+ %i.fa.fa-angle-double-left
8
+ %li
9
+ = link_to(job_failures_path(error_type: error_type, offset: previous_offset), class: "btn btn-default #{page_nav_disabled_class(pagination[:offset], 0)}", title: 'Previous') do
10
+ %i.fa.fa-angle-left
11
+ %li
12
+ = link_to(job_failures_path(error_type: error_type, offset: next_offset), class: "btn btn-default #{page_nav_disabled_class(pagination[:offset], pagination[:total])}", title: 'Next') do
13
+ %i.fa.fa-angle-right
14
+ %li
15
+ = link_to(job_failures_path(error_type: error_type, offset: pagination[:total]), class: "btn btn-default #{page_nav_disabled_class(pagination[:offset], pagination[:total])}", title: 'Last') do
16
+ %i.fa.fa-angle-double-right
@@ -0,0 +1,32 @@
1
+ .row
2
+ .col-md-4
3
+ .job-list
4
+ .list-top
5
+ %ol.breadcrumb
6
+ %li= link_to(job_title(@job), job_path(@job))
7
+ %li.active Failures
8
+ .clearfix
9
+ .list
10
+ - @slice_errors.each do |slice_error|
11
+ = link_to(job_failures_path(@job, error_type: slice_error['_id']['error_class']), class: "card callout") do
12
+ .inner
13
+ .title
14
+ .lead
15
+ = slice_error['_id']['error_class']
16
+
17
+ .description
18
+ = "Occurences: #{slice_error['count'].to_i}"
19
+
20
+ - if @failure_exception
21
+ .col-md-8
22
+ .job-status
23
+ .failures
24
+ .lead= @failure_exception['class_name']
25
+ .pagination-buttons.pull-right= render partial: 'pagination', locals: { error_type: @failure_exception['class_name'], pagination: @pagination }
26
+
27
+ .clearfix
28
+ .message
29
+ %pre
30
+ %code= @failure_exception['message']
31
+ .error
32
+ %pre.small= @failure_exception['backtrace'].join("\n")
@@ -1,9 +1,11 @@
1
- #queue
2
- = render partial: 'list', locals: { jobs: @jobs }
1
+ .row
2
+ #queue
3
+ = render partial: 'list', locals: { jobs: @jobs }
3
4
 
4
- .col-md-8.job-status
5
- #job
6
- .welcome_screen
7
- = link_to image_tag('rocket_job_mission_control/rocket-icon-512x512.png'), 'http://rocketjob.io'
8
- %h1 Welcome to rocketjob
9
- %p High volume, priority based, Enterprise Batch Processing solution for Ruby
5
+ .col-md-8
6
+ .job-status
7
+ #job
8
+ .welcome_screen
9
+ = link_to image_tag('rocket_job_mission_control/rocket-icon-512x512.png'), 'http://rocketjob.io'
10
+ %h1 Welcome to rocketjob
11
+ %p High volume, priority based, Enterprise Batch Processing solution for Ruby
@@ -34,4 +34,4 @@
34
34
  %td= slice.worker_name
35
35
  %td
36
36
  %pre
37
- %code= pretty_print_array_or_hash(slice.to_a)
37
+ %code= display_slice_info(slice, job.encrypt?)
@@ -1,50 +1,54 @@
1
- #queue
2
- = render partial: 'list', locals: { jobs: @jobs }
1
+ .row
2
+ #queue
3
+ = render partial: 'list', locals: { jobs: @jobs }
3
4
 
4
- .col-md-8.job-status
5
- #job
6
- .lead= job_title(@job)
5
+ .col-md-8.job-status
6
+ #job
7
+ .lead= job_title(@job)
7
8
 
8
- .job-state.inline-block
9
- .left state
10
- .right{ class: @job.state.to_s }
11
- = @job.state
9
+ .job-state.inline-block
10
+ .left state
11
+ .right{ class: @job.state.to_s }
12
+ = @job.state
12
13
 
13
- .priority.pull-right
14
- = form_for(@job, as: :job, url: job_path(@job), html: {method: :patch}) do |f|
14
+ .priority.pull-right
15
+ = form_for(@job, as: :job, url: job_path(@job), html: {method: :patch}) do |f|
15
16
 
16
- .input-group.spinner{ "data-id" => @job.id }
17
- = f.text_field(:priority, class: 'form-control')
17
+ .input-group.priority-group
18
+ = f.text_field(:priority, class: 'form-control')
18
19
 
19
- .input-group-btn-vertical
20
- %button#increase_priority.btn.btn-default.up{ type: 'button' }
21
- %i.fa.fa-caret-up
20
+ .input-group-btn-vertical
21
+ %button#increase_priority.btn.btn-default.up{ type: 'button' }
22
+ %i.fa.fa-caret-up
22
23
 
23
- %button#decrease_priority.btn.btn-default.down{ type: 'button' }
24
- %i.fa.fa-caret-down
24
+ %button#decrease_priority.btn.btn-default.down{ type: 'button' }
25
+ %i.fa.fa-caret-down
25
26
 
26
- = submit_tag "set", class: "btn btn-sm btn-primary priority-set"
27
+ = submit_tag "set", class: "btn btn-sm btn-primary priority-set"
27
28
 
28
- .job-retry.btn-group.pull-right{ style: 'margin-right: 1em;' }
29
- - valid_events = @job.aasm.events.collect{ |e| e.name }
30
- - if valid_events.include?(:abort)
31
- = link_to "abort", abort_job_path(@job), method: :patch, class: 'btn btn-default'
29
+ .job-actions.btn-group.pull-right{ style: 'margin-right: 1em;' }
30
+ - valid_events = @job.aasm.events.collect{ |e| e.name }
31
+ - if valid_events.include?(:abort)
32
+ = job_action_link('abort', rocket_job_mission_control.abort_job_path(@job), :patch)
32
33
 
33
- - if @job.completed? || @job.aborted?
34
- = link_to "destroy", job_path(@job), method: :delete, class: 'btn btn-default'
34
+ - if @job.completed? || @job.aborted?
35
+ = job_action_link('destroy', rocket_job_mission_control.job_path(@job), :delete)
35
36
 
36
- - if valid_events.include?(:fail)
37
- = link_to "fail", fail_job_path(@job), method: :patch, class: 'btn btn-default'
37
+ - if valid_events.include?(:fail)
38
+ = job_action_link('fail', rocket_job_mission_control.fail_job_path(@job), :patch)
38
39
 
39
- - if valid_events.include?(:pause)
40
- = link_to "pause", pause_job_path(@job), method: :patch, class: 'btn btn-default'
40
+ - if valid_events.include?(:pause)
41
+ = job_action_link('pause', rocket_job_mission_control.pause_job_path(@job), :patch)
41
42
 
42
- - if valid_events.include?(:resume)
43
- = link_to "resume", resume_job_path(@job), method: :patch, class: 'btn btn-default'
43
+ - if valid_events.include?(:resume)
44
+ = job_action_link('resume', rocket_job_mission_control.resume_job_path(@job), :patch)
44
45
 
45
- - if valid_events.include?(:retry)
46
- = link_to "retry", retry_job_path(@job), method: :patch, class: 'btn btn-default'
46
+ - if valid_events.include?(:retry)
47
+ = job_action_link('retry', rocket_job_mission_control.retry_job_path(@job), :patch)
47
48
 
48
- .clearfix
49
+ - if @job.failed? && @job.kind_of?(RocketJob::SlicedJob)
50
+ = link_to("view errors", job_failures_path(@job), class: 'btn btn-default')
49
51
 
50
- = render partial: 'status', locals: { job: @job }
52
+ .clearfix
53
+
54
+ = render partial: 'status', locals: { job: @job }
@@ -1,9 +1,9 @@
1
1
  - if worker.stopping?
2
2
  %p Worker is stopping...
3
- = link_to "destroy", worker_path(worker), method: :delete, class: 'btn btn-danger'
3
+ = link_to "destroy", rocket_job_mission_control.worker_path(worker), method: :delete, class: 'btn btn-danger'
4
4
  -else
5
- = link_to "stop", stop_worker_path(worker), method: :patch, class: 'btn btn-danger'
5
+ = link_to "stop", rocket_job_mission_control.stop_worker_path(worker), method: :patch, class: 'btn btn-danger'
6
6
  - if worker.paused?
7
- = link_to "resume", resume_worker_path(worker), method: :patch, class: 'btn btn-default'
7
+ = link_to "resume", rocket_job_mission_control.resume_worker_path(worker), method: :patch, class: 'btn btn-default'
8
8
  - else
9
- = link_to "pause", pause_worker_path(worker), method: :patch, class: 'btn btn-default'
9
+ = link_to "pause", rocket_job_mission_control.pause_worker_path(worker), method: :patch, class: 'btn btn-default'
@@ -1,51 +1,56 @@
1
1
  #workers
2
2
  - if @workers.present?
3
- .col-lg-1
4
- .dropdown
5
- %button.btn.btn-primary.dropdown-toggle{ data: {toggle: 'dropdown'} }
6
- Actions
7
- %span.caret
8
- %ul.dropdown-menu
9
- %li= link_to("Stop All", update_all_workers_path(worker_action: :stop), method: :patch)
10
- %li= link_to("Pause All", update_all_workers_path(worker_action: :pause), method: :patch)
11
- %li= link_to("Resume All", update_all_workers_path(worker_action: :resume), method: :patch)
3
+ .row
4
+ .col-xs-12
5
+ %ol.breadcrumb{ style: 'margin-bottom: 0'}
6
+ %li.active Workers
7
+ .btn-group.pull-right
8
+ - [:stop, :pause, :resume].each do |action|
9
+ = link_to("#{action.to_s.capitalize} All",
10
+ rocket_job_mission_control.update_all_workers_path(worker_action: action),
11
+ method: :patch,
12
+ data: { confirm: t(:confirm, scope: [:worker, :update_all], action: action) },
13
+ class: 'btn btn-default')
14
+ .clearfix
12
15
 
13
16
  .grid
14
- - @workers.each do |worker|
15
- .card.col-xs-12.col-sm-6.col-md-3.col-lg-3
16
- .inner.callout-top{ class: worker_card_class(worker) }
17
- .icon
18
- %i.fa.fa-database
19
-
20
- .title
21
- %h3
22
- = worker.name
23
-
24
- .state
25
- %b State:
26
- = worker.state
27
-
28
- .threads.max_threads
29
- %b Max Threads:
30
- = worker.max_threads
31
-
32
- .threads.current_threads
33
- %b Current Threads:
34
- = worker.heartbeat.current_threads.present? ? worker.heartbeat.current_threads : 0
35
-
36
- .time
37
- %b Started:
38
- = worker.started_at.strftime("%b, %d %Y at %l:%M %p")
39
-
40
- .actions
41
- = render partial: 'actions', locals: { worker: worker }
17
+ - @workers.each_slice(4) do |workers|
18
+ .row
19
+ - workers.each do |worker|
20
+ .card.col-xs-12.col-sm-6.col-md-3.col-lg-3
21
+ .inner.callout-top{ class: worker_card_class(worker) }
22
+ .title
23
+ %h3
24
+ = worker.name
25
+
26
+ .state
27
+ %b State:
28
+ = worker.state
29
+
30
+ .threads.max_threads
31
+ %b Max Threads:
32
+ = worker.max_threads
33
+
34
+ .threads.current_threads
35
+ %b Current Threads:
36
+ = worker.heartbeat.current_threads.to_i
37
+
38
+ .time
39
+ %b Started:
40
+ = worker.started_at.strftime("%b, %d %Y at %l:%M %p")
41
+
42
+ .actions
43
+ = render partial: 'actions', locals: { worker: worker }
42
44
 
43
45
  - else
44
- .no-workers
45
- There are currently no workers.
46
- %br
47
- You can start a worker with the following command:
48
-
49
- .code-block.col-md-6.col-md-offset-3
50
- %pre
51
- %code.language-bash bin/rocketjob
46
+ .row
47
+ .col-md-6.col-md-offset-3
48
+ .panel.panel-default
49
+ .panel-body
50
+ .lead
51
+ There are currently no workers.
52
+ You can start a worker with the following command:
53
+
54
+ .code-block
55
+ %pre
56
+ %code.language-bash bin/rocketjob
@@ -52,6 +52,28 @@ en:
52
52
  update_all:
53
53
  success: "Workers have been %{worker_action}."
54
54
  invalid: "Action not allowed."
55
+ confirm: "Are you sure you want to %{action} all workers?"
55
56
  job:
56
57
  find:
57
- :failure: "Could not find job with id: %{id}!"
58
+ failure: "Could not find job with id: %{id}!"
59
+ action:
60
+ confirm: "Are you sure you want to %{action} this job?"
61
+
62
+ dirmon_entry:
63
+ find:
64
+ failure: "Could not find entry with id: %{id}!"
65
+ create:
66
+ success: "Dirmon successfully created."
67
+ failure: "Error attempting to create."
68
+ destroy:
69
+ success: "Dirmon successfully destroyed."
70
+ failure: "Error attempting to destroy Dirmon."
71
+ enable:
72
+ success: "Dirmon successfully enabled."
73
+ failure: "Error attempting to enable Dirmon."
74
+ disable:
75
+ success: "Dirmon successfully disabled."
76
+ failure: "Error attempting to disable Dirmon."
77
+ update:
78
+ success: "Dirmon has been updated."
79
+ invalid: "Error attempting to update Dirmon."
data/config/routes.rb CHANGED
@@ -11,6 +11,7 @@ RocketJobMissionControl::Engine.routes.draw do
11
11
  collection do
12
12
  get :running
13
13
  end
14
+ resources :failures, controller: 'jobs/failures', only: [:index]
14
15
  end
15
16
 
16
17
  resources :workers, only: [:index, :destroy] do
@@ -24,5 +25,12 @@ RocketJobMissionControl::Engine.routes.draw do
24
25
  end
25
26
  end
26
27
 
28
+ resources :dirmon_entries do
29
+ member do
30
+ put :enable
31
+ put :disable
32
+ end
33
+ end
34
+
27
35
  root to: "jobs#index"
28
36
  end
@@ -1,3 +1,3 @@
1
1
  module RocketJobMissionControl
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0'
3
3
  end