cukedep 0.1.10 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +4 -0
  3. data/Rakefile +4 -6
  4. data/lib/cukedep/application.rb +89 -91
  5. data/lib/cukedep/cli/cmd-line.rb +0 -7
  6. data/lib/cukedep/config.rb +70 -74
  7. data/lib/cukedep/constants.rb +1 -1
  8. data/lib/cukedep/cuke-runner.rb +0 -5
  9. data/lib/cukedep/customization.rb +0 -5
  10. data/lib/cukedep/feature-model.rb +1 -7
  11. data/lib/cukedep/feature-rep.rb +0 -4
  12. data/lib/cukedep/file-action.rb +209 -221
  13. data/lib/cukedep/gherkin-facade.rb +7 -10
  14. data/lib/cukedep/gherkin-listener.rb +82 -89
  15. data/lib/cukedep/hook-dsl.rb +0 -4
  16. data/lib/cukedep/sandbox.rb +0 -5
  17. data/sample/features/step_definitions/steps.rb +30 -26
  18. data/sample/features/support/env.rb +1 -1
  19. data/sample/model/model.rb +198 -205
  20. data/sample/result.html +1 -1
  21. data/spec/cukedep/application_spec.rb +15 -16
  22. data/spec/cukedep/cli/cmd-line_spec.rb +13 -16
  23. data/spec/cukedep/cuke-runner_spec.rb +55 -61
  24. data/spec/cukedep/customization_spec.rb +21 -26
  25. data/spec/cukedep/debug-file-action.rb +12 -10
  26. data/spec/cukedep/feature-model_spec.rb +13 -16
  27. data/spec/cukedep/feature-rep_spec.rb +4 -7
  28. data/spec/cukedep/file-action_spec.rb +18 -34
  29. data/spec/cukedep/file-parsing.rb +9 -11
  30. data/spec/cukedep/gherkin-facade_spec.rb +3 -8
  31. data/spec/cukedep/gherkin-listener_spec.rb +10 -12
  32. data/spec/cukedep/hook-dsl_spec.rb +6 -9
  33. data/spec/cukedep/sample_features/cukedep.rake +44 -37
  34. data/spec/cukedep/sample_features/cukedep_hooks.rb +2 -2
  35. data/spec/cukedep/sample_features/dependencies.dot +1 -1
  36. data/templates/rake.erb +36 -29
  37. metadata +2 -2
@@ -4,25 +4,24 @@ require 'gherkin'
4
4
  require 'gherkin/lexer/encoding'
5
5
 
6
6
  module Cukedep # This module is used as a namespace
7
-
8
7
  # Facade design pattern: A facade is an object that provides
9
8
  # a simplified interface to a larger body of code.
10
9
  # Here the GherkinFacade class provides basic parsing service.
11
10
  class GherkinFacade
12
11
  # Indicate whether the parsing must be verbose or silent
13
12
  attr_reader(:verbose)
14
-
13
+
15
14
  # (External) encoding of the feature files.
16
15
  # It is a string that represents the name of an encoding
17
16
  # as expected by the mode argument of the IO#new method
18
17
  attr_reader(:external_encoding)
19
-
18
+
20
19
 
21
20
  def initialize(isVerbose, anExternalEncoding)
22
21
  @verbose = isVerbose
23
22
  @external_encoding = anExternalEncoding
24
23
  end
25
-
24
+
26
25
  # Parse feature files from the work directory that match
27
26
  # one of the given file name patterns.
28
27
  # Parse events are sent to the passed listener object.
@@ -34,22 +33,20 @@ module Cukedep # This module is used as a namespace
34
33
  # List all .feature files in work directory that match the pattern
35
34
  filenames = []
36
35
  file_patterns.each { |patt| filenames.concat(Dir.glob(patt)) }
37
-
36
+
38
37
  # Parse them
39
38
  filenames.each do |fname|
40
39
  puts " #{fname}" if verbose
41
40
  # To prevent encoding issue, open the file
42
41
  # with an explicit external encoding
43
- File.open(fname, "r:#{external_encoding}") do |f|
44
- parser.parse(f.read, fname, 0)
42
+ File.open(fname, "r:#{external_encoding}") do |f|
43
+ parser.parse(f.read, fname, 0)
45
44
  end
46
45
  end
47
-
46
+
48
47
  return aListener
49
48
  end
50
-
51
49
  end # class
52
-
53
50
  end # module
54
51
 
55
52
  # End of file
@@ -4,95 +4,88 @@ require_relative 'feature-rep'
4
4
 
5
5
 
6
6
  module Cukedep # This module is used as a namespace
7
-
8
-
9
- class FeatureFileRep
10
- attr_reader(:filepath)
11
- attr(:feature, true)
12
-
13
- def initialize(aFilepath)
14
- @filepath = aFilepath
15
- end
16
-
17
- def basename()
18
- File.basename(filepath)
19
- end
20
- end # class
21
-
22
- # A ParserListener listens to all the formatting events
23
- # emitted by the Gherkin parser.
24
- # It converts the received the feature file elements and builds
25
- # a representation of the feature files that is appropriate
26
- # for the Cukedep application.
27
- class GherkinListener
28
- # The list of feature files encountered so far
29
- attr_reader(:feature_files)
30
-
31
- # Internal representation of the feature being parsed
32
- attr(:current_feature, true)
33
-
34
- def initialize()
35
- @feature_files = []
36
- end
37
-
38
- ######################################
39
- # Event listening methods
40
- ######################################
41
-
42
- # Called when beginning the parsing of a feature file.
43
- # featureURI: path + filename of feature file.
44
- def uri(featureURI)
45
- new_file = FeatureFileRep.new(featureURI)
46
- feature_files << new_file
47
- end
48
-
49
- # aFeature is a Gherkin::Formatter::Model::Feature instance
50
- def feature(aFeature)
51
- tag_names = aFeature.tags.map(&:name)
52
- @current_feature = feature_files.last.feature = FeatureRep.new(tag_names)
53
- end
54
-
55
- # aBackground is a Gherkin::Formatter::Model::Background instance
56
- def background(_aBackground)
57
- ; # Do nothing
58
- end
59
-
60
- # aScenario is a Gherkin::Formatter::Model::Scenario instance
61
- def scenario(_aScenario)
62
- ; # Do nothing
63
- end
64
-
65
- # aScenarioOutline is a Gherkin::Formatter::Model::ScenarioOutline instance
66
- def scenario_outline(_aScenarioOutline)
67
- ; # Do nothing
68
- end
69
-
70
- # theExamples is a Gherkin::Formatter::Model::Examples instance
71
- def examples(_theExamples)
72
- ; # Do nothing
73
- end
74
-
75
- # aStep is a Gherkin::Formatter::Model::Step instance
76
- def step(_aStep)
77
- ; # Do nothing
78
- end
79
-
80
- # End of feature file notification.
81
- def eof()
82
- ; # Do nothing
83
- end
84
-
85
-
86
- # Catch all method
87
- def method_missing(message, *_args)
88
- puts "Method #{message} is not implemented (yet)."
89
- end
90
-
91
- end # class
92
-
7
+ class FeatureFileRep
8
+ attr_reader(:filepath)
9
+ attr(:feature, true)
10
+
11
+ def initialize(aFilepath)
12
+ @filepath = aFilepath
13
+ end
14
+
15
+ def basename()
16
+ File.basename(filepath)
17
+ end
18
+ end # class
19
+
20
+ # A ParserListener listens to all the formatting events
21
+ # emitted by the Gherkin parser.
22
+ # It converts the received the feature file elements and builds
23
+ # a representation of the feature files that is appropriate
24
+ # for the Cukedep application.
25
+ class GherkinListener
26
+ # The list of feature files encountered so far
27
+ attr_reader(:feature_files)
28
+
29
+ # Internal representation of the feature being parsed
30
+ attr(:current_feature, true)
31
+
32
+ def initialize()
33
+ @feature_files = []
34
+ end
35
+
36
+ ######################################
37
+ # Event listening methods
38
+ ######################################
39
+
40
+ # Called when beginning the parsing of a feature file.
41
+ # featureURI: path + filename of feature file.
42
+ def uri(featureURI)
43
+ new_file = FeatureFileRep.new(featureURI)
44
+ feature_files << new_file
45
+ end
46
+
47
+ # aFeature is a Gherkin::Formatter::Model::Feature instance
48
+ def feature(aFeature)
49
+ tag_names = aFeature.tags.map(&:name)
50
+ @current_feature = feature_files.last.feature = FeatureRep.new(tag_names)
51
+ end
52
+
53
+ # aBackground is a Gherkin::Formatter::Model::Background instance
54
+ def background(_aBackground)
55
+ ; # Do nothing
56
+ end
57
+
58
+ # aScenario is a Gherkin::Formatter::Model::Scenario instance
59
+ def scenario(_aScenario)
60
+ ; # Do nothing
61
+ end
62
+
63
+ # aScenarioOutline is a Gherkin::Formatter::Model::ScenarioOutline instance
64
+ def scenario_outline(_aScenarioOutline)
65
+ ; # Do nothing
66
+ end
67
+
68
+ # theExamples is a Gherkin::Formatter::Model::Examples instance
69
+ def examples(_theExamples)
70
+ ; # Do nothing
71
+ end
72
+
73
+ # aStep is a Gherkin::Formatter::Model::Step instance
74
+ def step(_aStep)
75
+ ; # Do nothing
76
+ end
77
+
78
+ # End of feature file notification.
79
+ def eof()
80
+ ; # Do nothing
81
+ end
82
+
83
+
84
+ # Catch all method
85
+ def method_missing(message, *_args)
86
+ puts "Method #{message} is not implemented (yet)."
87
+ end
88
+ end # class
93
89
  end # module
94
90
 
95
-
96
-
97
-
98
91
  # End of file
@@ -2,7 +2,6 @@
2
2
 
3
3
 
4
4
  module Cukedep # Module used as a namespace
5
-
6
5
  # Mix-in module that defines the DSL (Domain-Specific Language)
7
6
  # for specifying Cukedep hooks.
8
7
  # A hook is a custom code block that is executed when
@@ -74,9 +73,6 @@ module HookDSL
74
73
  handler = hooks.nil? ? nil : hooks.fetch(aScope)
75
74
  return handler
76
75
  end
77
-
78
76
  end # module
79
-
80
77
  end # module
81
-
82
78
  # End of file
@@ -2,10 +2,8 @@
2
2
 
3
3
 
4
4
  module Cukedep # This module is used as a namespace
5
-
6
5
  # A context in which hook block codes are run.
7
6
  class Sandbox
8
-
9
7
  attr_reader(:base_dir)
10
8
  attr_reader(:proj_dir)
11
9
 
@@ -13,9 +11,6 @@ module Cukedep # This module is used as a namespace
13
11
  @base_dir = theBaseDir.dup.freeze
14
12
  @proj_dir = theProjectDir.dup.freeze
15
13
  end
16
-
17
14
  end # class
18
-
19
15
  end # module
20
-
21
16
  # End of file
@@ -1,50 +1,54 @@
1
1
  # File: steps.rb
2
2
  # Step definitions for a sample Cucumber application
3
3
 
4
+ def store()
5
+ $store
6
+ end
7
+
4
8
  Given(/^the catalogue is empty$/) do
5
- $store.zap_catalogue!()
9
+ store.zap_catalogue!
6
10
  end
7
11
 
8
12
  Given(/^I add the video "(.*?)" to the catalogue$/) do |a_title|
9
- $store.add_video(a_title)
13
+ store.add_video(a_title)
10
14
  end
11
15
 
12
16
  Then(/^I should see the video "(.*?)" as unknown$/) do |a_title|
13
- expect($store.search_video(a_title)).to be_nil
17
+ expect(store.search_video(a_title)).to be_nil
14
18
  end
15
19
 
16
20
  Then(/^I should see the video "(.*?)" as (available)$/) do |a_title, a_state|
17
- found_video = $store.search_video(a_title)
21
+ found_video = store.search_video(a_title)
18
22
  expect(found_video.state).to eq(a_state.to_sym)
19
23
  end
20
24
 
21
25
  When(/^I remove the video "(.*?)"$/) do |a_title|
22
- found_video = $store.search_video(a_title)
26
+ found_video = store.search_video(a_title)
23
27
  expect(found_video).not_to be_nil
24
28
  expect(found_video.state).to eq(:available)
25
- $store.remove_video(found_video)
29
+ store.remove_video(found_video)
26
30
  end
27
31
 
28
32
  Given(/^there is no member yet$/) do
29
- $store.send(:zap_members!) # Why is this method seen as private?
33
+ store.send(:zap_members!) # Why is this method seen as private?
30
34
  end
31
35
 
32
36
  Then(/^I should see member "(.*?)" as unknown$/) do |member_name|
33
- expect($store.search_member(member_name)).to be_nil
37
+ expect(store.search_member(member_name)).to be_nil
34
38
  end
35
39
 
36
40
  Then(/^I should see member "(.*?)" as registered$/) do |member_name|
37
- expect($store.search_member(member_name)).not_to be_nil
41
+ expect(store.search_member(member_name)).not_to be_nil
38
42
  puts "Member #{member_name} is registered."
39
43
  end
40
44
 
41
45
  Given(/^I subscribe "(.*?)"$/) do |member_name|
42
- $store.add_member(member_name)
46
+ store.add_member(member_name)
43
47
  end
44
48
 
45
49
 
46
50
  Given(/^there is no registered user$/) do
47
- $store.zap_users!
51
+ store.zap_users!
48
52
  end
49
53
 
50
54
  When(/^I enter the credentials "(.*?)"$/) do |credential|
@@ -52,54 +56,54 @@ When(/^I enter the credentials "(.*?)"$/) do |credential|
52
56
  end
53
57
 
54
58
  Then(/^I should not be authorized$/) do
55
- expect($store.search_user(@entered_credential)).to be_nil
56
- puts "Invalid user credential"
59
+ expect(store.search_user(@entered_credential)).to be_nil
60
+ puts 'Invalid user credential'
57
61
  end
58
62
 
59
63
  When(/^I register my credentials "(.*?)"$/) do |credential|
60
- $store.add_user(credential)
64
+ store.add_user(credential)
61
65
  end
62
66
 
63
67
  Then(/^I should see a welcome message$/) do
64
- expect($store.search_user(@entered_credential)).not_to be_nil
65
- puts "Welcome to the rental application."
68
+ expect(store.search_user(@entered_credential)).not_to be_nil
69
+ puts 'Welcome to the rental application.'
66
70
  end
67
71
 
68
72
 
69
73
  When(/^I register the rental of "(.*?)" for "(.*?)"$/) do |a_title, member_name|
70
- found_video = $store.search_video(a_title)
74
+ found_video = store.search_video(a_title)
71
75
  expect(found_video).not_to be_nil
72
76
  expect(found_video.state).to eq(:available)
73
77
 
74
- member = $store.search_member(member_name)
78
+ member = store.search_member(member_name)
75
79
  expect(member).not_to be_nil
76
- $store.add_rental(found_video, member)
80
+ store.add_rental(found_video, member)
77
81
  @confirm_rental = true
78
82
  end
79
83
 
80
84
  Then(/^I should see the rental confirmed$/) do
81
- puts "Rental registered." if @confirm_rental
85
+ puts 'Rental registered.' if @confirm_rental
82
86
  @confirm_rental = nil
83
87
  end
84
88
 
85
89
  Then(/^I should see the rental refused$/) do
86
- puts "Rental refused." unless @confirm_rental
90
+ puts 'Rental refused.' unless @confirm_rental
87
91
  end
88
92
 
89
93
 
90
- When(/^I register the return of "(.*?)" from "(.*?)"$/) do |a_title, member_name|
91
- rental = $store.search_rental(a_title)
94
+ When(/^I register the return of "(.*?)" from "(.*?)"$/) do |title, member_name|
95
+ rental = store.search_rental(title)
92
96
  expect(rental).not_to be_nil
93
97
  expect(rental.member).to eq(member_name)
94
- $store.close_rental(rental)
98
+ store.close_rental(rental)
95
99
  @confirm_return = true
96
100
  end
97
101
 
98
102
  Then(/^I should see the return confirmed$/) do
99
- puts "Return registered." if @confirm_return
103
+ puts 'Return registered.' if @confirm_return
100
104
  @confirm_return = nil
101
105
  end
102
106
 
103
107
 
104
108
 
105
- # End of file
109
+ # End of file
@@ -9,4 +9,4 @@ AfterConfiguration do |_|
9
9
  $store = Sample::RentalStore.new
10
10
  end
11
11
 
12
- # End of file
12
+ # End of file
@@ -1,216 +1,209 @@
1
1
  # File: model.rb
2
2
 
3
3
  require 'pp'
4
- require 'yaml' # Rely on YAML for object persistence
4
+ require 'yaml' # Rely on YAML for object persistence
5
5
 
6
6
 
7
7
  # Module used as a namespace
8
8
  module Sample
9
+ # Assumption: the store has one examplar only of a video title
10
+ Video = Struct.new(
11
+ :title, # Title of the video (as an identifier)
12
+ :state # Current state of the video
13
+ )
14
+
15
+ # A member of the video store, i.e. a person that is allowed
16
+ # to rent a video from the store.
17
+ Member = Struct.new(
18
+ :name # As an identifier
19
+ )
20
+
21
+ # Association object between a Video and a Member.
22
+ # In our sample model, no rental history is performed
23
+ Rental = Struct.new(
24
+ :video, # Use the title of the rented video as key
25
+ :member # Use the name of the Member as key
26
+ )
27
+
28
+ # The identification of a store rental employee
29
+ # that is authorized to use the rental software.
30
+ User = Struct.new(
31
+ :credential # user credential
32
+ )
33
+
34
+ # Simplistic domain model of a video rental store.
35
+ class RentalStore
36
+ MyDir = File.dirname(__FILE__) + '/'
37
+ CatalogueFile = 'catalogue.yml'
38
+ MembersFile = 'members.yml'
39
+ RentalsFile = 'rentals.yml'
40
+ UsersFile = 'users.yml'
41
+
42
+
43
+ # The list of all videos owned by the store.
44
+ attr_reader(:catalogue)
45
+
46
+ # The list of all Members
47
+ attr_reader(:members)
48
+
49
+ # The list of all open-standing rentals
50
+ attr_reader(:rentals)
51
+
52
+ # The list of application user (credentials)
53
+ attr_reader(:users)
54
+
55
+ def initialize()
56
+ init_catalogue
57
+ init_members
58
+ init_rentals
59
+ init_users
60
+ end
61
+
62
+
63
+ # Clear the catalogue (= make it empty).
64
+ def zap_catalogue!()
65
+ catalogue.clear
66
+ save_catalogue
67
+ zap_rentals!
68
+ end
69
+
70
+ def zap_members!()
71
+ members.clear
72
+ save_members
73
+ zap_rentals!
74
+ end
75
+
76
+ def zap_users!()
77
+ users.clear
78
+ save_users
79
+ end
80
+
81
+ # Search a Video object having the given title
82
+ def search_video(aTitle)
83
+ result = catalogue.find { |video| video.title.strip == aTitle.strip }
84
+ if result.nil?
85
+ msg = "Video with title '#{aTitle}' isn't in the catalogue."
86
+ $stderr.puts msg
87
+ end
88
+
89
+ return result
90
+ end
91
+
92
+ def remove_video(aVideo)
93
+ catalogue.delete(aVideo)
94
+ save_catalogue
95
+ end
96
+
97
+ # Add a new video to the catalogue
98
+ def add_video(aTitle)
99
+ # Simplification: no check for title collision
100
+ catalogue << Video.new(aTitle, :available)
101
+ save_catalogue
102
+ end
103
+
104
+ def search_member(aName)
105
+ mb = members.find { |person| person.name.strip == aName.strip }
106
+ puts "No member with name #{aName}." if mb.nil?
107
+
108
+ return mb
109
+ end
9
110
 
10
- # Assumption: the store has one examplar only of a video title
11
- Video = Struct.new(
12
- :title, # Title of the video (as an identifier)
13
- :state # Current state of the video
14
- )
15
-
16
- # A member of the video store, i.e. a person that is allowed
17
- # to rent a video from the store.
18
- Member = Struct.new(
19
- :name # As an identifier
20
- )
21
-
22
- # Association object between a Video and a Member.
23
- # In our sample model, no rental history is performed
24
- Rental = Struct.new(
25
- :video, # Use the title of the rented video as key
26
- :member # Use the name of the Member as key
27
- )
28
-
29
- # The identification of a store rental employee
30
- # that is authorized to use the rental software.
31
- User = Struct.new(
32
- :credential # user credential
33
- )
34
-
35
- # Simplistic domain model of a video rental store.
36
- class RentalStore
37
- MyDir = File.dirname(__FILE__) + '/'
38
- CatalogueFile = 'catalogue.yml'
39
- MembersFile = 'members.yml'
40
- RentalsFile = 'rentals.yml'
41
- UsersFile = 'users.yml'
42
-
43
-
44
- # The list of all videos owned by the store.
45
- attr_reader(:catalogue)
46
-
47
- # The list of all Members
48
- attr_reader(:members)
49
-
50
- # The list of all open-standing rentals
51
- attr_reader(:rentals)
52
-
53
- # The list of application user (credentials)
54
- attr_reader(:users)
55
-
56
- def initialize()
57
- init_catalogue()
58
- init_members()
59
- init_rentals()
60
- init_users()
61
- end
62
-
63
-
64
- # Clear the catalogue (= make it empty).
65
- def zap_catalogue!()
66
- catalogue.clear()
67
- save_catalogue()
68
- zap_rentals!()
69
- end
70
-
71
- def zap_members!()
72
- members.clear()
73
- save_members()
74
- zap_rentals!()
75
- end
76
-
77
- def zap_users!()
78
- users.clear()
79
- save_users()
80
- end
81
-
82
- # Search a Video object having the given title
83
- def search_video(aTitle)
84
- result = catalogue.find {|video| video.title.strip == aTitle.strip}
85
- if result.nil?
86
- msg = "Video with title '#{aTitle}' isn't in the catalogue."
87
- $stderr.puts msg
88
- end
89
-
90
- return result
91
- end
92
-
93
- def remove_video(aVideo)
94
- catalogue.delete(aVideo)
95
- save_catalogue()
96
- end
97
-
98
- # Add a new video to the catalogue
99
- def add_video(aTitle)
100
- # Simplification: no check for title collision
101
- catalogue << Video.new(aTitle, :available)
102
- save_catalogue()
103
- end
104
-
105
- def search_member(aName)
106
- mb = members.find {|person| person.name.strip == aName.strip}
107
- puts "No member with name #{aName}." if mb.nil?
108
-
109
- return mb
110
- end
111
-
112
- def add_member(aName)
113
- # Simplification: no check for name collision
114
- members << Member.new(aName)
115
- save_members()
116
- end
117
-
118
- def search_user(aCredential)
119
- users.find {|user| user.credential.strip == aCredential.strip}
120
- end
121
-
122
- def add_user(aCredential)
123
- # Simplification: no check for credential collision
124
- users << User.new(aCredential)
125
- save_users()
126
- end
127
-
128
- def search_rental(aTitle)
129
- rentals.find {|r| r.video.strip == aTitle.strip}
130
- end
131
-
132
- def add_rental(aVideo, aMember)
133
- rentals << Rental.new(aVideo.title, aMember.name)
134
- aVideo.state = :rented
135
- save_rentals
136
- end
137
-
138
- def close_rental(aRental)
139
- returned_video = search_video(aRental.video)
140
- returned_video.state = :available
141
- rentals.delete(aRental)
142
- save_rentals
143
- end
144
-
145
- private
146
- def init_catalogue()
147
- filepath = MyDir + CatalogueFile
148
- if File.exist?(filepath)
149
- @catalogue = YAML.load_file(filepath)
150
- else
151
- @catalogue = []
152
- end
153
- end
154
-
155
- def init_members()
156
- filepath = MyDir + MembersFile
157
- if File.exist?(filepath)
158
- @members = YAML.load_file(filepath)
159
- else
160
- @members = []
161
- end
162
- end
163
-
164
- def init_users()
165
- filepath = MyDir + UsersFile
166
- if File.exist?(filepath)
167
- @users = YAML.load_file(filepath)
168
- else
169
- @users = []
170
- end
171
- end
172
-
173
- def init_rentals()
174
- filepath = MyDir + RentalsFile
175
- if File.exist?(filepath)
176
- @rentals = YAML.load_file(filepath)
177
- else
178
- @rentals = []
179
- end
180
- end
181
-
182
- def zap_rentals!()
183
- rentals.clear
184
- save_rentals()
185
- end
186
-
187
- def zap_members!()
188
- members.clear
189
- save_members()
190
- end
191
-
192
- def save_catalogue()
193
- filepath = MyDir + CatalogueFile
194
- File.open(filepath, 'w') {|f| YAML.dump(catalogue, f)}
195
- end
196
-
197
- def save_members()
198
- filepath = MyDir + MembersFile
199
- File.open(filepath, 'w') {|f| YAML.dump(members, f)}
200
- end
201
-
202
- def save_users()
203
- filepath = MyDir + UsersFile
204
- File.open(filepath, 'w') {|f| YAML.dump(users, f)}
205
- end
206
-
207
- def save_rentals()
208
- filepath = MyDir + RentalsFile
209
- File.open(filepath, 'w') {|f| YAML.dump(rentals, f)}
210
- end
211
-
212
- end # class
111
+ def add_member(aName)
112
+ # Simplification: no check for name collision
113
+ members << Member.new(aName)
114
+ save_members
115
+ end
116
+
117
+ def search_user(aCredential)
118
+ users.find { |user| user.credential.strip == aCredential.strip }
119
+ end
213
120
 
121
+ def add_user(aCredential)
122
+ # Simplification: no check for credential collision
123
+ users << User.new(aCredential)
124
+ save_users
125
+ end
126
+
127
+ def search_rental(aTitle)
128
+ rentals.find { |r| r.video.strip == aTitle.strip }
129
+ end
130
+
131
+ def add_rental(aVideo, aMember)
132
+ rentals << Rental.new(aVideo.title, aMember.name)
133
+ aVideo.state = :rented
134
+ save_rentals
135
+ end
136
+
137
+ def close_rental(aRental)
138
+ returned_video = search_video(aRental.video)
139
+ returned_video.state = :available
140
+ rentals.delete(aRental)
141
+ save_rentals
142
+ end
143
+
144
+ private
145
+
146
+ def init_catalogue()
147
+ filepath = MyDir + CatalogueFile
148
+ if File.exist?(filepath)
149
+ @catalogue = YAML.load_file(filepath)
150
+ else
151
+ @catalogue = []
152
+ end
153
+ end
154
+
155
+ def init_members()
156
+ filepath = MyDir + MembersFile
157
+ if File.exist?(filepath)
158
+ @members = YAML.load_file(filepath)
159
+ else
160
+ @members = []
161
+ end
162
+ end
163
+
164
+ def init_users()
165
+ filepath = MyDir + UsersFile
166
+ if File.exist?(filepath)
167
+ @users = YAML.load_file(filepath)
168
+ else
169
+ @users = []
170
+ end
171
+ end
172
+
173
+ def init_rentals()
174
+ filepath = MyDir + RentalsFile
175
+ if File.exist?(filepath)
176
+ @rentals = YAML.load_file(filepath)
177
+ else
178
+ @rentals = []
179
+ end
180
+ end
181
+
182
+ def zap_rentals!()
183
+ rentals.clear
184
+ save_rentals
185
+ end
186
+
187
+ def save_catalogue()
188
+ filepath = MyDir + CatalogueFile
189
+ File.open(filepath, 'w') { |f| YAML.dump(catalogue, f) }
190
+ end
191
+
192
+ def save_members()
193
+ filepath = MyDir + MembersFile
194
+ File.open(filepath, 'w') { |f| YAML.dump(members, f) }
195
+ end
196
+
197
+ def save_users()
198
+ filepath = MyDir + UsersFile
199
+ File.open(filepath, 'w') { |f| YAML.dump(users, f) }
200
+ end
201
+
202
+ def save_rentals()
203
+ filepath = MyDir + RentalsFile
204
+ File.open(filepath, 'w') { |f| YAML.dump(rentals, f) }
205
+ end
206
+ end # class
214
207
  end # module
215
208
 
216
- # End of file
209
+ # End of file