nestene 0.1.0

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 (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'