cukesalad 0.2.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 (76) hide show
  1. data/.rspec +1 -0
  2. data/Examples/Calculator/Gemfile +5 -0
  3. data/Examples/Calculator/Gemfile.lock +66 -0
  4. data/Examples/Calculator/cucumber.yml +4 -0
  5. data/Examples/Calculator/features/LOOK_MA_NO_STEP_DEFS.txt +2 -0
  6. data/Examples/Calculator/features/a_place_to_start.feature +9 -0
  7. data/Examples/Calculator/features/addition.feature +19 -0
  8. data/Examples/Calculator/features/complex_calculations.feature +16 -0
  9. data/Examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb +55 -0
  10. data/Examples/Calculator/features/lib/default/roles/calculating_individual.rb +32 -0
  11. data/Examples/Calculator/features/lib/default/tasks/add.rb +13 -0
  12. data/Examples/Calculator/features/lib/default/tasks/calculate.rb +4 -0
  13. data/Examples/Calculator/features/lib/default/tasks/calculations.rb +15 -0
  14. data/Examples/Calculator/features/lib/default/tasks/see_the_answer.rb +3 -0
  15. data/Examples/Calculator/features/lib/default/tasks/subtract.rb +4 -0
  16. data/Examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb +9 -0
  17. data/Examples/Calculator/features/subtraction.feature +20 -0
  18. data/Examples/Calculator/features/support/env.rb +6 -0
  19. data/Examples/Calculator/features/typical_workflow.feature +18 -0
  20. data/Examples/Calculator/lib/calculator.rb +53 -0
  21. data/Examples/Calculator/lib/config.ru +4 -0
  22. data/Examples/Calculator/lib/web_calculator.rb +82 -0
  23. data/Examples/Calculator/spec/calculator_spec.rb +73 -0
  24. data/Examples/Calculator/spec/web_calculator_spec.rb +99 -0
  25. data/Gemfile +9 -0
  26. data/Gemfile.lock +47 -0
  27. data/LICENSE +21 -0
  28. data/README.md +273 -0
  29. data/Rakefile +53 -0
  30. data/VERSION +1 -0
  31. data/bin/cukesalad +28 -0
  32. data/cucumber.yml +4 -0
  33. data/cukesalad/cli.rb +68 -0
  34. data/examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb +55 -0
  35. data/examples/Calculator/features/lib/default/roles/calculating_individual.rb +32 -0
  36. data/examples/Calculator/features/lib/default/tasks/add.rb +13 -0
  37. data/examples/Calculator/features/lib/default/tasks/calculate.rb +4 -0
  38. data/examples/Calculator/features/lib/default/tasks/calculations.rb +15 -0
  39. data/examples/Calculator/features/lib/default/tasks/see_the_answer.rb +3 -0
  40. data/examples/Calculator/features/lib/default/tasks/subtract.rb +4 -0
  41. data/examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb +9 -0
  42. data/examples/Calculator/features/support/env.rb +6 -0
  43. data/examples/Calculator/lib/calculator.rb +53 -0
  44. data/examples/Calculator/lib/web_calculator.rb +82 -0
  45. data/examples/Calculator/spec/calculator_spec.rb +73 -0
  46. data/examples/Calculator/spec/web_calculator_spec.rb +99 -0
  47. data/features/a_new_cukesalad_project.feature +34 -0
  48. data/features/define_a_role.feature +31 -0
  49. data/features/define_a_task.feature +55 -0
  50. data/features/lib/roles/step_free_cuker.rb +5 -0
  51. data/features/lib/tasks/create_a_file.rb +3 -0
  52. data/features/lib/tasks/create_a_new_cukesalad_project.rb +11 -0
  53. data/features/lib/tasks/create_a_role.rb +4 -0
  54. data/features/lib/tasks/create_a_task.rb +4 -0
  55. data/features/lib/tasks/create_directories.rb +5 -0
  56. data/features/lib/tasks/not_create_a_role.rb +2 -0
  57. data/features/lib/tasks/not_create_a_task.rb +3 -0
  58. data/features/lib/tasks/run.rb +4 -0
  59. data/features/lib/tasks/run_a_scenario.rb +7 -0
  60. data/features/lib/tasks/see_a_reply.rb +3 -0
  61. data/features/lib/tasks/see_the_step_has.rb +4 -0
  62. data/features/support/env.rb +7 -0
  63. data/lib/actor.rb +32 -0
  64. data/lib/codify/const_name.rb +21 -0
  65. data/lib/cukesalad.rb +33 -0
  66. data/lib/director.rb +28 -0
  67. data/lib/specifics.rb +30 -0
  68. data/lib/task_author.rb +12 -0
  69. data/spec/actor_spec.rb +72 -0
  70. data/spec/codify/as_const_name_spec.rb +28 -0
  71. data/spec/cukesalad/cli_spec.rb +99 -0
  72. data/spec/director_spec.rb +34 -0
  73. data/spec/spec_helper.rb +8 -0
  74. data/spec/specifics_spec.rb +36 -0
  75. data/spec/task_author_spec.rb +50 -0
  76. metadata +202 -0
@@ -0,0 +1,72 @@
1
+ $:.unshift(File.dirname(__FILE__), ".")
2
+ require 'spec_helper'
3
+
4
+ module SetThePlaceAtTheTable
5
+
6
+ def perform_task
7
+ place = []
8
+ place_the_fork_on_the_left_of place
9
+ place_the_knife_on_the_right_of place
10
+ place
11
+ end
12
+ end
13
+
14
+ module LayTheTable
15
+
16
+ def perform_task
17
+ value_of( :with_places_for ).to_i
18
+ end
19
+ end
20
+
21
+ module Butler
22
+
23
+ def place_the_fork_on_the_left_of place
24
+ place.unshift "fork"
25
+ end
26
+
27
+ def place_the_knife_on_the_right_of place
28
+ place.push "knife"
29
+ end
30
+
31
+ end
32
+
33
+ describe Actor do
34
+
35
+ it "gets into character" do
36
+ director = double("director")
37
+ role_description = "Butler"
38
+ director.should_receive( :explain_the_role ).with( role_description ).and_return( Butler )
39
+
40
+ actor = Actor.new role_description, director
41
+ end
42
+
43
+ it "performs a simple task as that character" do
44
+ role_description = "Butler"
45
+
46
+ task_description = "set the place at the table"
47
+
48
+ director = double("director")
49
+ director.should_receive( :explain_the_role ).with( role_description ).and_return( Butler )
50
+ director.should_receive( :how_do_i_perform ).with( task_description ).and_return( SetThePlaceAtTheTable )
51
+
52
+ actor = Actor.new role_description, director
53
+
54
+ actor.perform(task_description).should == ["fork","knife"]
55
+ end
56
+
57
+ it "can perform a task that requires certain details" do
58
+ role_description = "Butler"
59
+
60
+ task_description = "Lay The Table"
61
+ details = "with places for '5'"
62
+
63
+ director = double("director")
64
+ director.should_receive( :explain_the_role ).with( role_description ).and_return( Butler )
65
+ director.should_receive( :how_do_i_perform ).with( task_description ).and_return( LayTheTable )
66
+
67
+ actor = Actor.new role_description, director
68
+
69
+ actor.perform( task_description, details ).should == 5
70
+ end
71
+ end
72
+
@@ -0,0 +1,28 @@
1
+ $:.unshift(File.dirname(__FILE__), ".")
2
+ require 'spec_helper'
3
+ require 'codify/const_name'
4
+
5
+ describe Codify::ConstName do
6
+ include Codify
7
+
8
+ it "gives you ClassName from when it's already a class name" do
9
+ ConstName.from( "ClassName" ).should == "ClassName"
10
+ end
11
+
12
+ it "gives you ClassName when separated by spaces" do
13
+ ConstName.from( "Class Name" ).should == "ClassName"
14
+ end
15
+
16
+ it "gives you ClassName from lowercase class name" do
17
+ ConstName.from( "class name" ).should == "ClassName"
18
+ end
19
+
20
+ it "gives you ClassName from lower case class name with leading/trailing spaces" do
21
+ ConstName.from( " class name " ).should == "ClassName"
22
+ end
23
+
24
+ it "formats as a ClassName from a mix of class format and lower case" do
25
+ ConstName.from( "AnotherClass name " ).should == "AnotherClassName"
26
+ end
27
+ end
28
+
@@ -0,0 +1,99 @@
1
+ $:.unshift(File.dirname(__FILE__), ".")
2
+ require 'spec_helper'
3
+ require 'cukesalad/cli'
4
+
5
+ # don't override aruba path
6
+ module CukeSalad
7
+ class CLI
8
+ class Structure
9
+ def initialize
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ def dir_exists? file
16
+ File.exists?("tmp/aruba/"+file)
17
+ end
18
+
19
+ def file_content file
20
+ IO.read("tmp/aruba/"+file)
21
+ end
22
+
23
+ alias :file_exists? :dir_exists?
24
+
25
+ describe CukeSalad::CLI do
26
+
27
+ shared_examples_for "setup cukesalad and cucumber" do
28
+ it 'creates features directory' do
29
+ dir_exists?(features).should be_true
30
+ end
31
+
32
+ context 'in features' do
33
+ it 'creates support directory' do
34
+ dir_exists?(features+'/support').should be_true
35
+ end
36
+
37
+ context 'in support' do
38
+ let(:file) { features+'/support/env.rb' }
39
+
40
+ it 'creates a file env.rb' do
41
+ file_exists?(file).should be_true
42
+ end
43
+
44
+ it 'env.rb has the right content' do
45
+ content = "require 'cukesalad'\n begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end"
46
+ file_content(file).should == content
47
+ end
48
+ end
49
+
50
+ it 'creates lib directory' do
51
+ dir_exists?(features+'/lib').should be_true
52
+ end
53
+
54
+ context 'in lib' do
55
+ it 'creates default directory' do
56
+ dir_exists?(features+'/lib/default').should be_true
57
+ end
58
+
59
+ context 'in features/lib/default' do
60
+ it 'creates roles directory' do
61
+ dir_exists?(features+'/lib/default/roles').should be_true
62
+ end
63
+
64
+ it 'creates tasks directory' do
65
+ dir_exists?(features+'/lib/default/tasks').should be_true
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'Create new project foobar' do
73
+ let(:new_project) { 'foobar' }
74
+ let(:features) { new_project + "/features" }
75
+
76
+ before(:all) do
77
+ CukeSalad::CLI.create_new_project(new_project)
78
+ end
79
+
80
+ it "creates the project directory" do
81
+ dir_exists?(new_project).should be_true
82
+ end
83
+
84
+ it_should_behave_like "setup cukesalad and cucumber"
85
+ end
86
+
87
+ context 'Setup existing project' do
88
+ let(:features) { 'features' }
89
+
90
+ before(:all) do
91
+ CukeSalad::CLI.configure_existing_project
92
+ end
93
+ it_should_behave_like "setup cukesalad and cucumber"
94
+ end
95
+
96
+ after(:all) do
97
+ FileUtils.rm_rf 'tmp'
98
+ end
99
+ end
@@ -0,0 +1,34 @@
1
+ $:.unshift(File.dirname(__FILE__), ".")
2
+ require 'spec_helper'
3
+
4
+ class Something
5
+ end
6
+ class Another
7
+ end
8
+ class MoreThanOneWord
9
+ end
10
+
11
+ describe Director do
12
+
13
+ before :each do
14
+ @director = Director.new
15
+ end
16
+
17
+ it "gives you directives (i.e. a class) for Something" do
18
+ the_thing_we_found = @director.how_do_i_perform "something"
19
+ the_thing_we_found.should == Something
20
+ end
21
+
22
+ it "finds directives (i.e. a class) for something described with more than one word" do
23
+ the_thing_we_found = @director.how_do_i_perform "more than one word"
24
+ the_thing_we_found.should == MoreThanOneWord
25
+ end
26
+
27
+ it "apologises when it can't find the role" do
28
+ lambda { @director.explain_the_role "non existent role" }.should raise_error RuntimeError
29
+ end
30
+
31
+ it "apologises when it can't find the task" do
32
+ lambda { @director.how_do_i_perform "non existent task" }.should raise_error RuntimeError
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
6
+ require 'actor'
7
+ require 'director'
8
+ require 'specifics'
@@ -0,0 +1,36 @@
1
+ $:.unshift(File.dirname(__FILE__), ".")
2
+ require 'spec_helper'
3
+
4
+ class NeedingSpecifics
5
+ include Specifics
6
+ end
7
+
8
+ describe Specifics do
9
+
10
+ it "has an item of specific information" do
11
+ something = NeedingSpecifics.new
12
+ something.understand_the "specific 'information'"
13
+ something.value_of(:specific).should == "information"
14
+ end
15
+
16
+ it "has items of specific information" do
17
+ something = NeedingSpecifics.new
18
+ something.understand_the "first 'item' second 'another'"
19
+ something.value_of(:first).should == "item"
20
+ something.value_of(:second).should == "another"
21
+ end
22
+
23
+ it "copes with names having more than one word" do
24
+ something = NeedingSpecifics.new
25
+ something.understand_the "first thing 'item' second thing 'another'"
26
+ something.value_of(:first_thing).should == "item"
27
+ something.value_of(:second_thing).should == "another"
28
+ end
29
+
30
+ it "should cope with values having more than one word" do
31
+ something = NeedingSpecifics.new
32
+ something.understand_the "first thing 'item' second thing 'another thing'"
33
+ something.value_of(:first_thing).should == "item"
34
+ something.value_of(:second_thing).should == "another thing"
35
+ end
36
+ end
@@ -0,0 +1,50 @@
1
+ $:.unshift(File.dirname(__FILE__), ".")
2
+ require 'spec_helper'
3
+ require 'task_author'
4
+
5
+ class SomeActor
6
+ def see_how_to_do something
7
+ extend something
8
+ end
9
+ def do_your_thing
10
+ perform_task
11
+ end
12
+ def value_of thing
13
+ "this is the info"
14
+ end
15
+ end
16
+
17
+ describe 'TaskAuthor' do
18
+
19
+ it "creates a module by the same name" do
20
+ in_order_to "SomeTask" do;end
21
+
22
+ SomeActor.new.see_how_to_do SomeTask
23
+ end
24
+
25
+ it "creates a module by a similar name" do
26
+ in_order_to "some different task" do;end
27
+
28
+ SomeActor.new SomeDifferentTask
29
+ end
30
+
31
+ it "enables the actor to do something" do
32
+ in_order_to "SomeOtherTask" do
33
+ "done"
34
+ end
35
+
36
+ actor = SomeActor.new
37
+ actor.see_how_to_do SomeOtherTask
38
+ actor.do_your_thing.should == "done"
39
+ end
40
+
41
+ it "maps some attributes" do
42
+ in_order_to "YetAnotherTask", with_info: :info do
43
+ the :info
44
+ end
45
+
46
+ actor = SomeActor.new
47
+ actor.see_how_to_do YetAnotherTask
48
+ actor.do_your_thing.should == "this is the info"
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,202 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cukesalad
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.2.0
6
+ platform: ruby
7
+ authors:
8
+ - RiverGlide
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-20 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: cucumber
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.10.0
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - "="
33
+ - !ruby/object:Gem::Version
34
+ version: 2.5.0
35
+ type: :runtime
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: aruba
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ version: 0.3.5
46
+ type: :runtime
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: bundler
50
+ prerelease: false
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.0.0
57
+ type: :development
58
+ version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: jeweler
61
+ prerelease: false
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 1.5.2
68
+ type: :development
69
+ version_requirements: *id005
70
+ description: CukeSalad allows you to focus on the tasks at hand - expressing examples, the roles involved in those examples and the tasks that those roles need to perform with the product under development.
71
+ email: talktous@riverglide.com
72
+ executables:
73
+ - cukesalad
74
+ extensions: []
75
+
76
+ extra_rdoc_files:
77
+ - LICENSE
78
+ - README.md
79
+ files:
80
+ - .rspec
81
+ - Examples/Calculator/Gemfile
82
+ - Examples/Calculator/Gemfile.lock
83
+ - Examples/Calculator/cucumber.yml
84
+ - Examples/Calculator/features/LOOK_MA_NO_STEP_DEFS.txt
85
+ - Examples/Calculator/features/a_place_to_start.feature
86
+ - Examples/Calculator/features/addition.feature
87
+ - Examples/Calculator/features/complex_calculations.feature
88
+ - Examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb
89
+ - Examples/Calculator/features/lib/default/roles/calculating_individual.rb
90
+ - Examples/Calculator/features/lib/default/tasks/add.rb
91
+ - Examples/Calculator/features/lib/default/tasks/calculate.rb
92
+ - Examples/Calculator/features/lib/default/tasks/calculations.rb
93
+ - Examples/Calculator/features/lib/default/tasks/see_the_answer.rb
94
+ - Examples/Calculator/features/lib/default/tasks/subtract.rb
95
+ - Examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb
96
+ - Examples/Calculator/features/subtraction.feature
97
+ - Examples/Calculator/features/support/env.rb
98
+ - Examples/Calculator/features/typical_workflow.feature
99
+ - Examples/Calculator/lib/calculator.rb
100
+ - Examples/Calculator/lib/config.ru
101
+ - Examples/Calculator/lib/web_calculator.rb
102
+ - Examples/Calculator/spec/calculator_spec.rb
103
+ - Examples/Calculator/spec/web_calculator_spec.rb
104
+ - Gemfile
105
+ - Gemfile.lock
106
+ - LICENSE
107
+ - README.md
108
+ - Rakefile
109
+ - VERSION
110
+ - bin/cukesalad
111
+ - cucumber.yml
112
+ - cukesalad/cli.rb
113
+ - features/a_new_cukesalad_project.feature
114
+ - features/define_a_role.feature
115
+ - features/define_a_task.feature
116
+ - features/lib/roles/step_free_cuker.rb
117
+ - features/lib/tasks/create_a_file.rb
118
+ - features/lib/tasks/create_a_new_cukesalad_project.rb
119
+ - features/lib/tasks/create_a_role.rb
120
+ - features/lib/tasks/create_a_task.rb
121
+ - features/lib/tasks/create_directories.rb
122
+ - features/lib/tasks/not_create_a_role.rb
123
+ - features/lib/tasks/not_create_a_task.rb
124
+ - features/lib/tasks/run.rb
125
+ - features/lib/tasks/run_a_scenario.rb
126
+ - features/lib/tasks/see_a_reply.rb
127
+ - features/lib/tasks/see_the_step_has.rb
128
+ - features/support/env.rb
129
+ - lib/actor.rb
130
+ - lib/codify/const_name.rb
131
+ - lib/cukesalad.rb
132
+ - lib/director.rb
133
+ - lib/specifics.rb
134
+ - lib/task_author.rb
135
+ - spec/actor_spec.rb
136
+ - spec/codify/as_const_name_spec.rb
137
+ - spec/cukesalad/cli_spec.rb
138
+ - spec/director_spec.rb
139
+ - spec/spec_helper.rb
140
+ - spec/specifics_spec.rb
141
+ - spec/task_author_spec.rb
142
+ - examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb
143
+ - examples/Calculator/features/lib/default/roles/calculating_individual.rb
144
+ - examples/Calculator/features/lib/default/tasks/add.rb
145
+ - examples/Calculator/features/lib/default/tasks/calculate.rb
146
+ - examples/Calculator/features/lib/default/tasks/calculations.rb
147
+ - examples/Calculator/features/lib/default/tasks/see_the_answer.rb
148
+ - examples/Calculator/features/lib/default/tasks/subtract.rb
149
+ - examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb
150
+ - examples/Calculator/features/support/env.rb
151
+ - examples/Calculator/lib/calculator.rb
152
+ - examples/Calculator/lib/web_calculator.rb
153
+ - examples/Calculator/spec/calculator_spec.rb
154
+ - examples/Calculator/spec/web_calculator_spec.rb
155
+ homepage: https://github.com/RiverGlide/CukeSalad
156
+ licenses:
157
+ - MIT
158
+ post_install_message:
159
+ rdoc_options: []
160
+
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ none: false
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: "0"
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: "0"
175
+ requirements: []
176
+
177
+ rubyforge_project:
178
+ rubygems_version: 1.7.2
179
+ signing_key:
180
+ specification_version: 3
181
+ summary: Friction Free BDD/ATDD with cucumber
182
+ test_files:
183
+ - examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb
184
+ - examples/Calculator/features/lib/default/roles/calculating_individual.rb
185
+ - examples/Calculator/features/lib/default/tasks/add.rb
186
+ - examples/Calculator/features/lib/default/tasks/calculate.rb
187
+ - examples/Calculator/features/lib/default/tasks/calculations.rb
188
+ - examples/Calculator/features/lib/default/tasks/see_the_answer.rb
189
+ - examples/Calculator/features/lib/default/tasks/subtract.rb
190
+ - examples/Calculator/features/lib/default/tasks/switch_on_the_calculator.rb
191
+ - examples/Calculator/features/support/env.rb
192
+ - examples/Calculator/lib/calculator.rb
193
+ - examples/Calculator/lib/web_calculator.rb
194
+ - examples/Calculator/spec/calculator_spec.rb
195
+ - examples/Calculator/spec/web_calculator_spec.rb
196
+ - spec/actor_spec.rb
197
+ - spec/codify/as_const_name_spec.rb
198
+ - spec/cukesalad/cli_spec.rb
199
+ - spec/director_spec.rb
200
+ - spec/spec_helper.rb
201
+ - spec/specifics_spec.rb
202
+ - spec/task_author_spec.rb