nestene 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +31 -0
  7. data/Rakefile +2 -0
  8. data/TODO.txt +7 -0
  9. data/api.rb +8 -0
  10. data/autons.rb +13 -0
  11. data/bin/nestene.rb +5 -0
  12. data/config.ru +21 -0
  13. data/features/api/api_create_auton.feature +6 -0
  14. data/features/api/api_credentials.feature +11 -0
  15. data/features/api/api_list_of_autons.feature +6 -0
  16. data/features/callback.feature +13 -0
  17. data/features/create_auton.feature +11 -0
  18. data/features/credentials.feature +7 -0
  19. data/features/execute_step.feature +59 -0
  20. data/features/restart.feature +7 -0
  21. data/features/schedule_steps.feature +7 -0
  22. data/features/step_definitions/api/api_create_auton_steps.rb +16 -0
  23. data/features/step_definitions/api/api_credentials_steps.rb +22 -0
  24. data/features/step_definitions/api/api_list_of_autons_steps.rb +8 -0
  25. data/features/step_definitions/callback_steps.rb +75 -0
  26. data/features/step_definitions/create_auton_steps.rb +32 -0
  27. data/features/step_definitions/credentials_steps.rb +24 -0
  28. data/features/step_definitions/execute_step_steps.rb +177 -0
  29. data/features/step_definitions/restart_steps.rb +5 -0
  30. data/features/step_definitions/schedule_steps_steps.rb +29 -0
  31. data/features/step_definitions/time_delayed_steps_steps.rb +103 -0
  32. data/features/step_definitions/ui/create_auton_steps.rb +21 -0
  33. data/features/step_definitions/ui/list_of_autons_steps.rb +18 -0
  34. data/features/step_definitions/ui/schedule_auton_step_steps.rb +64 -0
  35. data/features/step_definitions/ui/show_auton_steps.rb +49 -0
  36. data/features/support/env.rb +36 -0
  37. data/features/time_delayed_steps.feature +28 -0
  38. data/features/ui/create_auton.feature +7 -0
  39. data/features/ui/list_of_autons.feature +7 -0
  40. data/features/ui/schedule_auton_step.feature +12 -0
  41. data/features/ui/show_auton.feature +14 -0
  42. data/lib/nestene/actor/auton_queue.rb +87 -0
  43. data/lib/nestene/actor/auton_storage.rb +50 -0
  44. data/lib/nestene/actor/core.rb +150 -0
  45. data/lib/nestene/actor/delayed_scheduler.rb +76 -0
  46. data/lib/nestene/auton_context.rb +31 -0
  47. data/lib/nestene/auton_execution_queue.rb +43 -0
  48. data/lib/nestene/auton_state.rb +27 -0
  49. data/lib/nestene/callback.rb +7 -0
  50. data/lib/nestene/delayed_method.rb +17 -0
  51. data/lib/nestene/executed_method.rb +29 -0
  52. data/lib/nestene/executing_method.rb +23 -0
  53. data/lib/nestene/execution_error.rb +39 -0
  54. data/lib/nestene/scheduled_method.rb +21 -0
  55. data/lib/nestene/storage.rb +72 -0
  56. data/lib/nestene/ui/app.rb +103 -0
  57. data/lib/nestene/ui/public/app/application.js +75 -0
  58. data/lib/nestene/ui/public/vendor/angular-ui-sortable/sortable.min.js +1 -0
  59. data/lib/nestene/ui/public/vendor/angular.min.js +248 -0
  60. data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap-theme.css +347 -0
  61. data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap-theme.min.css +7 -0
  62. data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap.css +5785 -0
  63. data/lib/nestene/ui/public/vendor/bootstrap/css/bootstrap.min.css +7 -0
  64. data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  65. data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +229 -0
  66. data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  67. data/lib/nestene/ui/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  68. data/lib/nestene/ui/public/vendor/bootstrap/js/bootstrap.js +1951 -0
  69. data/lib/nestene/ui/public/vendor/bootstrap/js/bootstrap.min.js +6 -0
  70. data/lib/nestene/ui/public/vendor/jquery-ui.min.js +13 -0
  71. data/lib/nestene/ui/public/vendor/jquery.bootstrap-growl.min.js +1 -0
  72. data/lib/nestene/ui/public/vendor/jquery.min.js +5 -0
  73. data/lib/nestene/ui/public/vendor/json-editor/directives.js +245 -0
  74. data/lib/nestene/ui/public/vendor/json-editor/styles.css +165 -0
  75. data/lib/nestene/ui/views/auton.haml +42 -0
  76. data/lib/nestene/ui/views/auton_form.haml +8 -0
  77. data/lib/nestene/ui/views/index.haml +35 -0
  78. data/lib/nestene/ui/views/layout.haml +40 -0
  79. data/lib/nestene/ui/views/schedule_step.haml +14 -0
  80. data/lib/nestene/version.rb +3 -0
  81. data/lib/nestene.rb +50 -0
  82. data/nestene.gemspec +39 -0
  83. data/spec/nestene/auton_execution_queue_spec.rb +78 -0
  84. data/spec/nestene/nestene_spec.rb +137 -0
  85. data/spec/nestene/storage_spec.rb +125 -0
  86. data/spec/spec_helper.rb +2 -0
  87. metadata +373 -0
@@ -0,0 +1,42 @@
1
+ .jumbotron
2
+ %p= @auton_id
3
+ %p
4
+ %span#auton_state.label.label-default= @auton_state.state
5
+ .panel.panel-default
6
+ .panel-heading Actions
7
+ .panel-body
8
+ %a.btn.btn-default#schedule_step{href: url('/auton/%s/schedule_step') % @auton_id} Schedule Step
9
+
10
+ .panel.panel-default
11
+ .panel-heading State
12
+ .panel-body
13
+ %code#current_state{'data-toggle' => "tooltip", 'data-placement' => "left", title:"Tooltip on left"}
14
+ = JSON.pretty_generate(@auton_state.serialized)
15
+
16
+ .panel.panel-default
17
+ #queue.panel-heading Queue
18
+ .panel-body.container-fluid
19
+ .row
20
+ .col-md-12
21
+ .panel.panel-default
22
+ #executing.panel-heading Executing
23
+ .panel-body
24
+ - if @auton_state.queue.currently_executing
25
+ = "%s(%s)" % [@auton_state.queue.currently_executing.name, @auton_state.queue.currently_executing.parameters.join(', ')]
26
+
27
+ .row
28
+ .col-md-6
29
+ #to_execute.panel.panel-default
30
+ .panel-heading To Execute
31
+ .panel-body
32
+ %ul.list-group
33
+ - @auton_state.queue.to_execute.each do |to_execute|
34
+ %li.list-group-item= to_execute.name
35
+
36
+ .col-md-6
37
+ #executed.panel.panel-default
38
+ .panel-heading Executed
39
+ .panel-body
40
+ %ul.list-group
41
+ - @auton_state.queue.executed.each do |executed|
42
+ %li.list-group-item= executed.name
@@ -0,0 +1,8 @@
1
+ .panel.panel-default
2
+ .panel-heading Create New Auton
3
+ .panel-body
4
+ %form{method: 'POST', action: url '/auton/create', role: 'form'}
5
+ .form-group
6
+ %label{for: 'auton_type'}Type
7
+ %input#auton_type.form-control{type: 'text', placeholder: 'Type', name: 'auton_type'}
8
+ %button#create.btn.btn-default{type: 'submit'}Create
@@ -0,0 +1,35 @@
1
+ .panel.panel-default{'ng-app' => 'nestene'}
2
+ .panel-body
3
+ %div#tabs{role: 'tabpanel'}
4
+ %ul.nav.nav-tabs{role: 'tablist'}
5
+ %li.active#running_nav{role: 'presentation'}
6
+ %a.btn.btn-default{href: "#running", 'data-toggle'=>"tab", role: 'tab'} Running Autons
7
+ %li#create_auton_nav{role: 'presentation'}
8
+ %a.btn.btn-default{href: "#create_auton", 'data-toggle'=>"tab", role: 'tab'} Create Auton
9
+ %li#credentials_nav{role: 'presentation'}
10
+ %a.btn.btn-default{href: "#credentials", 'data-toggle'=>"tab", role: 'tab'} Credentials
11
+ %div.tab-content
12
+ %div.tab-pane.active.fade.in#running{'ng-controller' => 'AutonsController'}
13
+ %ul.list-group#auton_list
14
+ %li.list-group-item{'ng-repeat' => 'autonName in autons'}
15
+ %a.btn{'ng-href' => url('/auton/{{autonName}}')} {{autonName}}
16
+ %div.tab-pane.fade#create_auton{'ng-controller' => 'CreateAutonFormController'}
17
+ .panel.panel-default
18
+ .panel-heading Create New Auton
19
+ .panel-body
20
+ %form{role: 'form'}
21
+ .form-group
22
+ %label{for: 'auton_type'} Auton Type
23
+ %input#auton_type.form-control{type: 'text', placeholder: 'Type', 'ng-model' => 'autonType', 'required' => true}
24
+ %button#create.btn.btn-default{type: 'submit', 'ng-click' => 'createAuton()'}Create
25
+ %div.tab-pane.fade#credentials{'ng-controller' => 'CredentialsController'}
26
+ .panel.panel-default
27
+ .panel-heading Current Credentials
28
+ .panel-body
29
+ .jsonView
30
+ %json{child: "credentials", 'default-collapsed'=>"false", type: "object"}
31
+ %button.btn.btn-default{'ng-click' => 'storeCredentials()'} Save
32
+
33
+
34
+
35
+
@@ -0,0 +1,40 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title Nestene
5
+ %meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
6
+ / Bootstrap
7
+ %link{:href => "/vendor/bootstrap/css/bootstrap.min.css", :rel => "stylesheet"}
8
+ %link{:href => "/vendor/json-editor/styles.css", :rel => "stylesheet"}
9
+ / HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries
10
+ / WARNING: Respond.js doesn't work if you view the page via file://
11
+ /[if lt IE 9]
12
+ <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
13
+ <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
14
+ %body
15
+ .container
16
+ / Static navbar
17
+ .navbar.navbar-default{:role => "navigation"}
18
+ .navbar-header
19
+ %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", :type => "button"}
20
+ %span.sr-only Toggle navigation
21
+ %span.icon-bar
22
+ %span.icon-bar
23
+ %span.icon-bar
24
+ %a.navbar-brand{:href => "/"} Nestene
25
+ %ol.breadcrumb
26
+ - (@position || [{link: '/', name: 'Home'}]).each do |crumb|
27
+ %li
28
+ %a.btn.btn-default.btn-xs{href: crumb[:link]}=crumb[:name]
29
+ %div
30
+ - if @error then
31
+ .alert.alert-error= @error
32
+ = yield
33
+ %script{:src => "/vendor/jquery.min.js"}
34
+ %script{:src => "/vendor/jquery-ui.min.js"}
35
+ %script{:src => "/vendor/angular.min.js"}
36
+ %script{:src => "/vendor/bootstrap/js/bootstrap.min.js"}
37
+ %script{:src => "/vendor/jquery.bootstrap-growl.min.js"}
38
+ %script{:src => "/vendor/angular-ui-sortable/sortable.min.js"}
39
+ %script{:src => "/vendor/json-editor/directives.js"}
40
+ %script{:src => "/app/application.js"}
@@ -0,0 +1,14 @@
1
+ .panel.panel-default
2
+ .panel-heading Schedule Step for #{@auton_id}
3
+ .panel-body
4
+ %form{method: 'POST', action: '/auton/%s/schedule_step' % @auton_id, role: 'form'}
5
+ .form-group
6
+ %label{for: 'step_name'}Step Name
7
+ %select.form-control{name: 'step_name'}
8
+ - @available_methods.each do |method|
9
+ %option{value: method.name, selected: @step_name == method.name.to_s}= method.name
10
+ .form-group
11
+ %label{for: 'step_name'} Parameters
12
+ - (0 .. @number_of_params - 1).each do |index|
13
+ %input.form-control{name: 'parameters[%d]' % index, type: 'text'}
14
+ %button#schedule.btn.btn-default{type: 'submit'}Schedule
@@ -0,0 +1,3 @@
1
+ module Nestene
2
+ VERSION = "0.1.0"
3
+ end
data/lib/nestene.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'structure_mapper'
2
+ require 'securerandom'
3
+ require 'celluloid'
4
+ require 'celluloid/autostart'
5
+ require 'nestene/storage'
6
+ require 'nestene/actor/delayed_scheduler'
7
+ require 'nestene/actor/core'
8
+ require 'nestene/actor/auton_storage'
9
+ require 'nestene/actor/auton_queue'
10
+ require 'nestene/auton_context'
11
+ require 'nestene/callback'
12
+ require 'nestene/scheduled_method'
13
+ require 'nestene/executing_method'
14
+ require 'nestene/execution_error'
15
+ require 'nestene/executed_method'
16
+ require 'nestene/auton_execution_queue'
17
+ require 'nestene/auton_state'
18
+ require 'nestene/ui/app'
19
+
20
+ module Nestene
21
+
22
+ class SelfValue
23
+ def value
24
+ self
25
+ end
26
+ end
27
+
28
+ def self.class_from_string(name)
29
+ name.split('::').inject(Object) do |mod, class_name|
30
+ mod.const_get(class_name) or raise "Could not find class #{name}"
31
+ end
32
+ end
33
+
34
+ def self.start_nestene(storage)
35
+ Celluloid::Actor[:nestene_core] = Nestene::Actor::Core.new(storage)
36
+ Celluloid::Actor[:auton_queue] = Nestene::Actor::AutonQueue.new
37
+ Celluloid::Actor[:delayed_scheduler] = Nestene::Actor::DelayedScheduler.new
38
+ end
39
+
40
+ end
41
+
42
+ class Time
43
+ def self.from_structure str
44
+ str ? iso8601(str) : nil
45
+ end
46
+
47
+ def to_structure
48
+ iso8601(3)
49
+ end
50
+ end
data/nestene.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nestene/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "nestene"
8
+ spec.version = Nestene::VERSION
9
+ spec.authors = ["Dragan Milic"]
10
+ spec.email = ["dragan@netice9.com"]
11
+ spec.summary = %q{Nestene Automation Framework}
12
+ spec.description = %q{Nestene Automation Framework}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_development_dependency "cucumber"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "pry-debugger"
28
+ spec.add_development_dependency "timecop"
29
+ spec.add_development_dependency "capybara"
30
+ spec.add_development_dependency "rack-test"
31
+ spec.add_development_dependency "poltergeist"
32
+
33
+ spec.add_dependency "sinatra"
34
+ spec.add_dependency "celluloid"
35
+ spec.add_dependency "structure_mapper"
36
+ spec.add_dependency "haml"
37
+ spec.add_dependency "puma"
38
+
39
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+ require 'timecop'
3
+
4
+ module Nestene
5
+
6
+
7
+ describe AutonExecutionQueue do
8
+
9
+ before do
10
+ Timecop.freeze(Time.local(1990))
11
+ end
12
+
13
+ after do
14
+ Timecop.return
15
+ end
16
+
17
+ describe '#remove_delayed_method' do
18
+ context "when the delayed method has been scheduled" do
19
+ let (:delayed_method_uuid) {subject.add_delayed_method(:some_method, ['p1','p2'], 0.3).uuid}
20
+
21
+ it "should remove the delayed method" do
22
+ uuid = delayed_method_uuid
23
+ subject.remove_delayed_method(uuid)
24
+
25
+ expect(subject.delayed).to be_empty
26
+
27
+ end
28
+ end
29
+ end
30
+
31
+
32
+ describe '#add_delayed_method' do
33
+
34
+ let(:every) { nil }
35
+
36
+ before {subject.add_delayed_method :some_method, ['p1','p2'], 0.3, every}
37
+
38
+ it "should add a delayed method" do
39
+ expect(subject.delayed).to_not be_empty
40
+ end
41
+
42
+ it "should assign proper execute_at to delayed method" do
43
+ expect(subject.delayed.first.execute_at).to eq(Time.now + 0.3)
44
+ end
45
+
46
+ it "should assign uuid to delayed method" do
47
+ expect(subject.delayed.first.uuid).to_not be_nil
48
+ end
49
+
50
+ it "should assign scheduled_at to delayed method" do
51
+ expect(subject.delayed.first.scheduled_at).to eq(Time.now)
52
+ end
53
+
54
+ it "should assign parameters to delayed method" do
55
+ expect(subject.delayed.first.parameters).to eq(["p1", "p2"])
56
+ end
57
+
58
+
59
+ it "should sort delayed methods by their execute_at times in ascending order" do
60
+ subject.add_delayed_method :sooner_method, ['p1','p2'], 0.1
61
+
62
+ expect(subject.delayed.first.name).to eq(:sooner_method)
63
+ end
64
+
65
+ context "when every is not nil" do
66
+
67
+ let(:every) { 0.5 }
68
+
69
+ it "should set every value" do
70
+ expect(subject.delayed.first.every).to eq(every)
71
+ end
72
+
73
+ end
74
+
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+ require 'timecop'
3
+
4
+ module Nestene
5
+
6
+ describe ScheduledMethod do
7
+ describe '#new' do
8
+ context "when DelayedMethod is passed as parameter" do
9
+
10
+ let(:delayed_method) do
11
+ method = DelayedMethod.new
12
+ method.name = :some_method
13
+ method.parameters = ['p1','p2']
14
+ method.uuid = 'some_uuid'
15
+ method.scheduled_at = Time.now
16
+ method
17
+ end
18
+
19
+ subject {described_class.new(delayed_method)}
20
+
21
+
22
+ it "should copy method's uuid" do
23
+ expect(subject.uuid).to eq(delayed_method.uuid)
24
+ end
25
+
26
+ it "should copy name" do
27
+ expect(subject.name).to eq(delayed_method.name)
28
+ end
29
+
30
+ it "should copy parameters" do
31
+ expect(subject.parameters).to eq(delayed_method.parameters)
32
+ end
33
+
34
+
35
+ end
36
+ end
37
+ end
38
+
39
+ describe ExecutingMethod do
40
+ describe :new do
41
+ context "when ScheduledMethod is passed as parameter" do
42
+ let (:callback) {Callback.new}
43
+ let (:scheduled) do
44
+ scheduled = ScheduledMethod.new
45
+ scheduled.callback = callback
46
+ scheduled
47
+ end
48
+
49
+ subject {ExecutingMethod.new(scheduled)}
50
+
51
+ it "should copy callback" do
52
+ expect(subject.callback).to eq(callback)
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ describe ExecutedMethod do
60
+ describe :new do
61
+ context "when ExecutingMethod is passed as parameter" do
62
+ let (:callback) {Callback.new}
63
+ let (:executed) do
64
+ executed = ExecutingMethod.new
65
+ executed.callback = callback
66
+ executed.started_at = Time.now
67
+ executed
68
+ end
69
+
70
+ subject {ExecutedMethod.new(executed)}
71
+
72
+ it "should copy callback" do
73
+ expect(subject.callback).to eq(callback)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ describe ExecutionError do
80
+
81
+ let(:exception) do
82
+ begin
83
+ raise Exception, "some message"
84
+ rescue Exception => e
85
+ e
86
+ end
87
+ end
88
+
89
+ context "when no error is passed to constructur" do
90
+ it "should have nil messsage" do
91
+ expect(subject.message).to be_nil
92
+ end
93
+ it "should have nil type" do
94
+ expect(subject.type).to be_nil
95
+ end
96
+ it "should have nil backtrace" do
97
+ expect(subject.backtrace).to be_nil
98
+ end
99
+ end
100
+
101
+ context 'when error is passed to constructor' do
102
+ subject { described_class.new(exception) }
103
+
104
+ it "should have message set" do
105
+ expect(subject.message).to eq('some message')
106
+ end
107
+
108
+ it "should have type set" do
109
+ expect(subject.type).to eq('Exception')
110
+ end
111
+
112
+ it "should have backtrace set" do
113
+ expect(subject.backtrace).not_to be_empty
114
+ end
115
+
116
+ end
117
+
118
+ describe :to_exception do
119
+ subject { ExecutionError.new(exception).to_exception }
120
+
121
+ it "should create" do
122
+ expect(subject).to be_a(Exception)
123
+ end
124
+
125
+ it "should create exception with the stored messsage" do
126
+ expect(subject.message).to eq("some message")
127
+ end
128
+
129
+ it "should create exception with a backtrace" do
130
+ expect(subject.backtrace).not_to be_empty
131
+ end
132
+
133
+
134
+
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ module Nestene
4
+
5
+ describe FileNameConverter do
6
+ describe '#key_to_file_name' do
7
+ it "should convert non-charater in the file name to hex-escaped version" do
8
+ expect(subject.key_to_file_name('test_-+/')).to eq('test_5f_2d_2b_2f')
9
+ end
10
+
11
+ end
12
+
13
+ describe '#file_name_to_key' do
14
+ it "should convert hex-escaped version of the file name to the original" do
15
+ expect(subject.file_name_to_key('test_5f_2d_2b_2f')).to eq('test_-+/')
16
+ end
17
+ end
18
+ end
19
+
20
+ describe DiskStorage do
21
+
22
+ let(:tmpdir) {Dir.mktmpdir}
23
+
24
+ after{FileUtils.remove_entry_secure tmpdir}
25
+
26
+ describe :list do
27
+
28
+ subject {DiskStorage.new(tmpdir)}
29
+
30
+ context "when there is nothing in the storage" do
31
+ it "should return empty list" do
32
+ expect(subject.list).to eq([])
33
+ end
34
+ end
35
+
36
+ context "when there is at least one object in the storage" do
37
+ before {subject.store("test",{})}
38
+
39
+ it "should return list of existing objects" do
40
+ expect(subject.list).to eq(['test'])
41
+ end
42
+ end
43
+ end
44
+
45
+ describe :load do
46
+
47
+ subject {DiskStorage.new(tmpdir)}
48
+
49
+ context "when the object does not exist" do
50
+ it "should return nil" do
51
+ expect(subject.load('whatever')).to be_nil
52
+ end
53
+ end
54
+
55
+ context "when the object exists" do
56
+ before {subject.store('test',{})}
57
+ it "should return object's structure" do
58
+ expect(subject.load('test')).to eq({})
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ describe :store do
65
+ subject {DiskStorage.new(tmpdir)}
66
+
67
+ it "stores structure" do
68
+ subject.store('test',{})
69
+ expect(subject.load('test')).to eq({})
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ describe MemoryStorage do
76
+ describe :list do
77
+
78
+ subject {MemoryStorage.new}
79
+
80
+ context "when there is nothing in the storage" do
81
+ it "should return empty list" do
82
+ expect(subject.list).to eq([])
83
+ end
84
+ end
85
+
86
+ context "when there is at least one object in the storage" do
87
+ before {subject.store("test",{})}
88
+
89
+ it "should return list of existing objects" do
90
+ expect(subject.list).to eq(['test'])
91
+ end
92
+ end
93
+ end
94
+
95
+ describe :load do
96
+
97
+ subject {MemoryStorage.new}
98
+
99
+ context "when the object does not exist" do
100
+ it "should return nil" do
101
+ expect(subject.load('whatever')).to be_nil
102
+ end
103
+ end
104
+
105
+ context "when the object exists" do
106
+ before {subject.store('test',{})}
107
+ it "should return object's structure" do
108
+ expect(subject.load('test')).to eq({})
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+
115
+ describe :store do
116
+ subject {MemoryStorage.new}
117
+
118
+ it "stores structure" do
119
+ subject.store('test',{})
120
+ expect(subject.load('test')).to eq({})
121
+ end
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,2 @@
1
+ require 'nestene'
2
+ require 'pry'