xliffer 1.0.1 → 1.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b50380a2269eafb1911a8f06333d9c2c3aac05ad
4
- data.tar.gz: d5d440b6d59e7720be43382854672c001a4148a8
3
+ metadata.gz: f2c02d93b1d9d6bab91e7f11acc68b009627647a
4
+ data.tar.gz: 47287acd5be0ca4262a2bdcfaf6e10f48ec0ce70
5
5
  SHA512:
6
- metadata.gz: ae05afe6a609feb192e2f8eb116012514d8ad9d7e156653bb065bbe9c0f7b777f4c1ea9a73d8c5d4926fd02b067e74813df2cf953e6b08078de9cf36937b0ccb
7
- data.tar.gz: 50740ecc58ee40c41cca7693ef2d539ddc25b580a2008761e3d3712a5fa2c150ed20db6a7ff9cefb630c3d2f47e188b5ab81311e0660b2747266fa962e910783
6
+ metadata.gz: f877905bc87525bd2771c8bd9f68ebfdf893147e73813ee8999f0c73636456d38e51aadca814874adf22bb5a34708600642e9e02e511ad4173f9cad28ea6040c
7
+ data.tar.gz: 3679c4fd443dd9c93788f758f55b09988ea31c252ff3a3dc75ad0e88676b59ab95575221ffca152fe95b3333cea82e9304bf19cb435bb164a59b7b848c1f21a4
data/Gemfile CHANGED
@@ -10,6 +10,6 @@ group :test do
10
10
  end
11
11
 
12
12
  group :development do
13
- gem 'rspec', '~> 2'
13
+ gem 'rspec', '~> 3'
14
14
  gem 'simplecov', '>= 0.9.0'
15
15
  end
data/README.md CHANGED
@@ -6,9 +6,7 @@
6
6
  xliffer
7
7
  =======
8
8
 
9
- A XLIFF parser gem.
10
-
11
- Currently implementing the [version 1.2](http://docs.oasis-open.org/xliff/xliff-core/xliff-core.html) from xliff. It is on a very alpha phase.
9
+ XLIFFer helps you to parse xliff files and get their content. You can read and modify your xliff files with it.
12
10
 
13
11
  Usage
14
12
  -----
@@ -30,9 +28,21 @@ puts file.source_language
30
28
  puts file.target_language
31
29
 
32
30
  # Prints all string and the translations on this file.
33
- file.strings.each do |string|
31
+ file.strings.each do |string|
34
32
  puts "#{string.source} => #{string.target}"
35
33
  end
34
+
35
+ # Changes the translation of texts "hi" to "oi"
36
+ file.strings.find_all { |s| s.source = "hi" }.each do |string|
37
+ string.target = "oi"
38
+ end
39
+
40
+ # Modifies the translation of a string in the file with the given id,
41
+ # or add the translation if don't exist
42
+ file['target-string-id'].target = "new translation to this id"
43
+
44
+ # Generate the new xml file with the change
45
+ puts xliff.to_s
36
46
  ```
37
47
 
38
48
 
@@ -40,5 +50,3 @@ Roadmap
40
50
  ------
41
51
 
42
52
  * Read all fields according to 1.2 specification
43
- * Be able to modify/add contents on the xliff files
44
- * Regenerate the XML from the file
@@ -1,3 +1,3 @@
1
1
  module XLIFFer
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
data/lib/xliffer/xliff.rb CHANGED
@@ -20,11 +20,7 @@ module XLIFFer
20
20
 
21
21
  private
22
22
  def parse(text)
23
- begin
24
- @xml = Nokogiri::XML(text)
25
- rescue
26
- fail FormatError, "Not a XML file"
27
- end
23
+ @xml = Nokogiri::XML(text)
28
24
 
29
25
  @xml.remove_namespaces!
30
26
  root = @xml.xpath('/xliff')
@@ -11,13 +11,31 @@ module XLIFFer
11
11
  raise ArgumentError, "can't create a File without a file subtree"
12
12
  end
13
13
 
14
- @original = xml.attr("original")
15
- @source_language = xml.attr("source-language")
16
- @target_language = xml.attr("target-language")
17
- @strings = xml.xpath('.//trans-unit').map{|tu| String.new(tu) }
14
+ @xml = xml
15
+
16
+ @original = @xml.attr("original")
17
+ @source_language = @xml.attr("source-language")
18
+ @target_language = @xml.attr("target-language")
19
+ @strings = @xml.xpath('.//trans-unit').map{|tu| String.new(tu) }
20
+ end
21
+
22
+ def [](id)
23
+ @strings.find { |s| s.id == id }
18
24
  end
19
25
 
26
+ def []=(id, target)
27
+ self[id].target = target
28
+ end
20
29
 
30
+ def source_language=(val)
31
+ @source_language = val
32
+ @xml['source-language'] = val
33
+ end
34
+
35
+ def target_language=(val)
36
+ @target_language = val
37
+ @xml['target-language'] = val
38
+ end
21
39
  private
22
40
 
23
41
  def file?(xml)
@@ -20,6 +20,12 @@ module XLIFFer
20
20
  @target = val
21
21
  end
22
22
 
23
+ def source=(val)
24
+ source = @xml.xpath('./source')
25
+ source.first.content = val
26
+ @source = val
27
+ end
28
+
23
29
  private
24
30
 
25
31
  def trans_unit?(xml)
@@ -0,0 +1,11 @@
1
+ <xliff version='1.2'>
2
+ <file original='hello.txt' source-language='en' target-language='fr' datatype='plaintext'>
3
+ <trans-unit id='hi'>
4
+ <source>Hello world</source>
5
+ <target>Bonjour le monde</target>
6
+ <alt-trans>
7
+ <target lang='es'>Hola mundo</target>
8
+ </alt-trans>
9
+ </trans-unit>
10
+ </file>
11
+ </xliff>
data/spec/spec_helper.rb CHANGED
@@ -11,7 +11,6 @@ require 'equivalent-xml/rspec_matchers'
11
11
  require 'xliffer'
12
12
 
13
13
  RSpec.configure do |config|
14
- config.treat_symbols_as_metadata_keys_with_true_values = true
15
14
  config.run_all_when_everything_filtered = true
16
15
  config.filter_run :focus
17
16
  config.order = 'random'
@@ -3,10 +3,10 @@ require 'nokogiri'
3
3
 
4
4
  module XLIFFer
5
5
  describe XLIFF::File do
6
- context "#new" do
6
+ describe "#new" do
7
7
  it "is created with a nokogiri file node" do
8
8
  file_node = Nokogiri::XML.parse("<file></file>").xpath("/file").first
9
- XLIFF::File.new(file_node).should be
9
+ expect(XLIFF::File.new(file_node)).to be
10
10
  end
11
11
 
12
12
  it "can't be created with a string" do
@@ -19,57 +19,137 @@ module XLIFFer
19
19
  end
20
20
  end
21
21
 
22
- context "#original" do
22
+ describe "#original" do
23
23
  it "is nil if not defined" do
24
24
  file_node = Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
25
- XLIFF::File.new(file_node).original.should be nil
25
+ expect(XLIFF::File.new(file_node).original).to be nil
26
26
  end
27
27
 
28
28
  it "is the original attribute on file tag" do
29
29
  file_node = Nokogiri::XML.parse('<xliff><file original="neat file.c"></file></xliff>').xpath("//file").first
30
- XLIFF::File.new(file_node).original.should eql("neat file.c")
30
+ expect(XLIFF::File.new(file_node).original).to eql("neat file.c")
31
31
  end
32
32
  end
33
33
 
34
- context "#original" do
34
+ describe "#original" do
35
35
  it "is nil if not defined" do
36
36
  file_node = Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
37
- XLIFF::File.new(file_node).original.should be nil
37
+ expect(XLIFF::File.new(file_node).original).to be nil
38
38
  end
39
39
 
40
40
  it "is the original attribute on file tag" do
41
41
  file_node = Nokogiri::XML.parse('<xliff><file original="neat file.c"></file></xliff>').xpath("//file").first
42
- XLIFF::File.new(file_node).original.should eql("neat file.c")
42
+ expect(XLIFF::File.new(file_node).original).to eql("neat file.c")
43
43
  end
44
44
  end
45
45
 
46
- context "#source_language" do
46
+ describe "#source_language" do
47
47
  it "is nil if not defined" do
48
48
  file_node = Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
49
- XLIFF::File.new(file_node).source_language.should be nil
49
+ expect(XLIFF::File.new(file_node).source_language).to be nil
50
50
  end
51
51
 
52
52
  it "is the original attribute on file tag" do
53
53
  file_node = Nokogiri::XML.parse('<xliff><file source-language="en"></file></xliff>').xpath("//file").first
54
- XLIFF::File.new(file_node).source_language.should eql("en")
54
+ expect(XLIFF::File.new(file_node).source_language).to eql("en")
55
55
  end
56
56
  end
57
57
 
58
- context "#target_language" do
58
+ describe "#target_language" do
59
59
  it "is nil if not defined" do
60
60
  file_node = Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
61
- XLIFF::File.new(file_node).target_language.should be nil
61
+ expect(XLIFF::File.new(file_node).target_language).to be nil
62
62
  end
63
63
 
64
64
  it "is the original attribute on file tag" do
65
65
  file_node = Nokogiri::XML.parse('<xliff><file target-language="fr"></file></xliff>').xpath("//file").first
66
- XLIFF::File.new(file_node).target_language.should eql("fr")
66
+ expect(XLIFF::File.new(file_node).target_language).to eql("fr")
67
67
  end
68
68
  end
69
69
 
70
- context "#strings" do
71
- before(:all) do
72
- @trans_unit = <<-EOF
70
+ describe "attribute accessors" do
71
+ let(:subject) do
72
+ XLIFF::File.new Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
73
+ end
74
+
75
+ describe "source_language=" do
76
+ it "changes the source language" do
77
+ file_node = Nokogiri::XML.parse('<xliff><file source-language="fr"></file></xliff>').xpath("//file").first
78
+ subject = XLIFF::File.new file_node
79
+ subject.source_language = 'en'
80
+ expect(subject.source_language).to eq('en')
81
+ end
82
+
83
+ it "adds source language if don't exist" do
84
+ subject.source_language = 'en'
85
+ expect(subject.source_language).to eq('en')
86
+ end
87
+ end
88
+
89
+ describe "target_language=" do
90
+ it "changes the target language" do
91
+ file_node = Nokogiri::XML.parse('<xliff><file target-language="fr"></file></xliff>').xpath("//file").first
92
+ subject = XLIFF::File.new file_node
93
+ subject.target_language = 'en'
94
+ expect(subject.target_language).to eq('en')
95
+ end
96
+
97
+ it "adds target language if don't exist" do
98
+ subject.target_language = 'en'
99
+ expect(subject.target_language).to eq('en')
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "string accessors" do
105
+ let(:xml) do
106
+ <<-EOF
107
+ <file>
108
+ <trans-unit id="hello">
109
+ <source>Hello World</source>
110
+ <target>Bonjour le monde</target>
111
+ </trans-unit>
112
+ <trans-unit id="bye">
113
+ <source>Bye World</source>
114
+ <target>Au revoir le monde</target>
115
+ </trans-unit>
116
+ <trans-unit id="missing">
117
+ <source>Missing</source>
118
+ </trans-unit>
119
+ </file>
120
+ EOF
121
+ end
122
+
123
+ let(:subject) do
124
+ XLIFF::File.new(Nokogiri::XML.parse(xml).xpath("//file").first)
125
+ end
126
+
127
+ describe "[]" do
128
+ it "gets the string with this id" do
129
+ expect(subject['hello'].target).to eq("Bonjour le monde")
130
+ end
131
+
132
+ it "returns nil if no string found" do
133
+ expect(subject['non-existent id']).to be_nil
134
+ end
135
+ end
136
+
137
+ describe "[]=" do
138
+ it "changes the string target" do
139
+ subject['hello'] = 'changed text'
140
+ expect(subject['hello'].target).to eq('changed text')
141
+ end
142
+
143
+ it "adds a text if don't exist" do
144
+ subject['missing'] = 'new text'
145
+ expect(subject['missing'].target).to eq('new text')
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "#strings" do
151
+ let(:trans_unit) do
152
+ <<-EOF
73
153
  <trans-unit id="my id">
74
154
  <source>Hello World</source>
75
155
  <target>Bonjour le monde</target>
@@ -78,22 +158,22 @@ module XLIFFer
78
158
  end
79
159
  it "is an array " do
80
160
  file_node = Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
81
- XLIFF::File.new(file_node).strings.should be_kind_of(Array)
161
+ expect(XLIFF::File.new(file_node).strings).to be_kind_of(Array)
82
162
  end
83
163
 
84
164
  it "can be empty" do
85
165
  file_node = Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
86
- XLIFF::File.new(file_node).strings.should be_empty
166
+ expect(XLIFF::File.new(file_node).strings).to be_empty
87
167
  end
88
168
 
89
169
  it "should have a string" do
90
- file_node = Nokogiri::XML.parse("<xliff><file>#{@trans_unit}</file></xliff>").xpath("//file").first
91
- XLIFF::File.new(file_node).strings.size.should eql(1)
170
+ file_node = Nokogiri::XML.parse("<xliff><file>#{trans_unit}</file></xliff>").xpath("//file").first
171
+ expect(XLIFF::File.new(file_node).strings.size).to eql(1)
92
172
  end
93
173
 
94
174
  it "should have multiple strings" do
95
- file_node = Nokogiri::XML.parse("<xliff><file>#{@trans_unit * 10}</file></xliff>").xpath("//file").first
96
- XLIFF::File.new(file_node).strings.size.should eql(10)
175
+ file_node = Nokogiri::XML.parse("<xliff><file>#{trans_unit * 10}</file></xliff>").xpath("//file").first
176
+ expect(XLIFF::File.new(file_node).strings.size).to eql(10)
97
177
  end
98
178
  end
99
179
  end
@@ -3,18 +3,19 @@ require 'nokogiri'
3
3
 
4
4
  module XLIFFer
5
5
  describe XLIFF::String do
6
- context "#new" do
7
- before(:all) do
8
- @minimal_trans_unit = <<-EOF
9
- <trans-unit id="my id">
10
- <source>Hello World</source>
11
- <target>Bonjour le monde</target>
12
- </trans-unit>
13
- EOF
14
- end
6
+ let(:trans_unit) do
7
+ <<-EOF
8
+ <trans-unit id="my id">
9
+ <source>Hello World</source>
10
+ <target>Bonjour le monde</target>
11
+ </trans-unit>
12
+ EOF
13
+ end
14
+
15
+ describe "#new" do
15
16
  it "is created with a nokogiri trans-unit node" do
16
- trans_unit_node = Nokogiri::XML.parse(@minimal_trans_unit).xpath("//trans-unit").first
17
- XLIFF::String.new(trans_unit_node).should be
17
+ trans_unit_node = Nokogiri::XML.parse(trans_unit).xpath("//trans-unit").first
18
+ expect(XLIFF::String.new(trans_unit_node)).to be
18
19
  end
19
20
 
20
21
  it "can't be created with a string" do
@@ -45,63 +46,46 @@ module XLIFFer
45
46
  end
46
47
  end
47
48
 
48
- context "#id" do
49
+ describe "#id" do
49
50
  it "is nil if not defined" do
50
51
  xml = "<trans-unit><source></source><target></target></trans-unit>"
51
52
  trans_unit_node = Nokogiri::XML.parse(xml).xpath("//trans-unit").first
52
- XLIFF::String.new(trans_unit_node).id.should be nil
53
+ expect(XLIFF::String.new(trans_unit_node).id).to be nil
53
54
  end
54
55
 
55
56
  it "is the id attribute on trans-unit tag" do
56
57
  xml = "<trans-unit id='my id'><source></source><target></target></trans-unit>"
57
58
  trans_unit_node = Nokogiri::XML.parse(xml).xpath("//trans-unit").first
58
- XLIFF::String.new(trans_unit_node).id.should eql("my id")
59
+ expect(XLIFF::String.new(trans_unit_node).id).to eql("my id")
59
60
  end
60
61
  end
61
62
 
62
- context "#strings" do
63
- before(:all) do
64
- @trans_unit = <<-EOF
65
- <trans-unit id="my id">
66
- <source>Hello World</source>
67
- <target>Bonjour le monde</target>
68
- </trans-unit>
69
- EOF
70
- end
71
-
63
+ describe "#target=" do
72
64
  it 'Modify target' do
73
- trans_unit_node = Nokogiri::XML.parse(@trans_unit).xpath("//trans-unit").first
65
+ trans_unit_node = Nokogiri::XML.parse(trans_unit).xpath("//trans-unit").first
74
66
  string = XLIFF::String.new(trans_unit_node)
75
67
  string.target = 'Hola Mundo'
76
- string.target.should eq 'Hola Mundo'
68
+ expect(string.target).to eq 'Hola Mundo'
77
69
  end
78
70
 
79
- it 'Modify target if xml doensnt contain target initially' do
80
- xml = "<trans-unit id='my id'><source>Value</source></trans-unit>"
81
- trans_unit_node = Nokogiri::XML.parse(xml).xpath("//trans-unit").first
82
- string = XLIFF::String.new(trans_unit_node)
83
- string.target = 'Hola Mundo'
84
- string.target.should eq 'Hola Mundo'
71
+ context 'when target do not exist' do
72
+ it 'add a new target' do
73
+ xml = "<trans-unit id='my id'><source>Value</source></trans-unit>"
74
+ trans_unit_node = Nokogiri::XML.parse(xml).xpath("//trans-unit").first
75
+ string = XLIFF::String.new(trans_unit_node)
76
+ string.target = 'Hola Mundo'
77
+ expect(string.target).to eq 'Hola Mundo'
78
+ end
85
79
  end
86
- # it "is an array " do
87
- # trans_unit_node = Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
88
- # XLIFF::File.new(trans_unit_node).strings.should be_kind_of(Array)
89
- # end
90
-
91
- # it "can be empty" do
92
- # trans_unit_node = Nokogiri::XML.parse("<xliff><file></file></xliff>").xpath("//file").first
93
- # XLIFF::File.new(trans_unit_node).strings.should be_empty
94
- # end
95
-
96
- # it "should have a string" do
97
- # trans_unit_node = Nokogiri::XML.parse("<xliff><file>#{@trans_unit}</file></xliff>").xpath("//file").first
98
- # XLIFF::File.new(trans_unit_node).strings.size.should eql(1)
99
- # end
80
+ end
100
81
 
101
- # it "should have multiple strings" do
102
- # trans_unit_node = Nokogiri::XML.parse("<xliff><file>#{@trans_unit * 10}</file></xliff>").xpath("//file").first
103
- # XLIFF::File.new(trans_unit_node).strings.size.should eql(10)
104
- # end
82
+ describe "#source=" do
83
+ it 'Modify source' do
84
+ trans_unit_node = Nokogiri::XML.parse(trans_unit).xpath("//trans-unit").first
85
+ string = XLIFF::String.new(trans_unit_node)
86
+ string.source = 'Hola Mundo'
87
+ expect(string.source).to eq 'Hola Mundo'
88
+ end
105
89
  end
106
90
  end
107
91
  end
@@ -2,13 +2,13 @@ require 'spec_helper'
2
2
 
3
3
  module XLIFFer
4
4
  describe XLIFF do
5
- context "#new" do
5
+ describe "#new" do
6
6
  it "accepts a xliff file" do
7
- XLIFF.new(::File.open("spec/files/empty.xliff")).should be
7
+ expect(XLIFF.new(::File.open("spec/files/empty.xliff"))).to be
8
8
  end
9
9
 
10
10
  it "accepts a xliff string" do
11
- XLIFF.new(::File.open("spec/files/empty.xliff").read).should be
11
+ expect(XLIFF.new(::File.open("spec/files/empty.xliff").read)).to be
12
12
  end
13
13
 
14
14
  it "doesn't accept a number" do
@@ -24,43 +24,60 @@ module XLIFFer
24
24
  end
25
25
  end
26
26
 
27
- context "#version" do
27
+ describe "#version" do
28
28
  it "is the xliff version" do
29
- XLIFF.new('<xliff version="9.8"></xliff>').version.should eql("9.8")
29
+ expect(XLIFF.new('<xliff version="9.8"></xliff>').version).to eql("9.8")
30
30
  end
31
31
 
32
32
  it "is nil when it is not present" do
33
- XLIFF.new('<xliff></xliff>').version.should be_nil
33
+ expect(XLIFF.new('<xliff></xliff>').version).to be_nil
34
34
  end
35
35
 
36
36
  it "is a string when there is a xliff version" do
37
- XLIFF.new('<xliff version="9.8"></xliff>').version.should be_kind_of(String)
37
+ expect(XLIFF.new('<xliff version="9.8"></xliff>').version).to be_kind_of(String)
38
38
  end
39
39
  end
40
40
 
41
- context "#files" do
41
+ describe "#files" do
42
42
  it "is an array " do
43
- XLIFF.new('<xliff></xliff>').files.should be_kind_of(Array)
43
+ expect(XLIFF.new('<xliff></xliff>').files).to be_kind_of(Array)
44
44
  end
45
45
 
46
46
  it "can be empty" do
47
- XLIFF.new('<xliff></xliff>').files.should be_empty
47
+ expect(XLIFF.new('<xliff></xliff>').files).to be_empty
48
48
  end
49
49
 
50
50
  it "should have a file" do
51
- XLIFF.new('<xliff><file></file></xliff>').files.first.should be_kind_of(XLIFF::File)
51
+ expect(XLIFF.new('<xliff><file></file></xliff>').files.first).to be_kind_of(XLIFF::File)
52
52
  end
53
53
 
54
54
  it "should have multiple files" do
55
- XLIFF.new('<xliff><file></file><file></file></xliff>').files.size.should eql(2)
55
+ expect(XLIFF.new('<xliff><file></file><file></file></xliff>').files.size).to eql(2)
56
56
  end
57
57
  end
58
58
 
59
- context "#regenate" do
60
- it 'should output an xml' do
61
- xml = ::File.open("spec/files/empty.xliff").read
59
+ describe "#to_s" do
60
+ it 'outputs a xml' do
61
+ xml = ::File.open("spec/files/simple.xliff").read
62
62
  xliff = XLIFF.new(xml)
63
- expect(xliff.to_s).to be_equivalent_to(xml)
63
+ expect(Nokogiri::XML(xliff.to_s)).to be
64
+ end
65
+
66
+ context 'when xml is not changed' do
67
+ it 'outputs the same xml' do
68
+ xml = ::File.open("spec/files/simple.xliff").read
69
+ xliff = XLIFF.new(xml)
70
+ expect(xliff.to_s).to be_equivalent_to(xml)
71
+ end
72
+ end
73
+
74
+ context 'when xml is changed' do
75
+ it 'outputs a different xml' do
76
+ xml = ::File.open("spec/files/simple.xliff").read
77
+ xliff = XLIFF.new(xml)
78
+ xliff.files.first.strings.first.target = "My brand new target"
79
+ expect(xliff.to_s).not_to be_equivalent_to(xml)
80
+ end
64
81
  end
65
82
  end
66
83
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xliffer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Tanus
@@ -73,6 +73,7 @@ files:
73
73
  - lib/xliffer/xliff/string.rb
74
74
  - spec/files/empty.xliff
75
75
  - spec/files/file.foobar
76
+ - spec/files/simple.xliff
76
77
  - spec/files/wiki.xliff
77
78
  - spec/files/xliffspec1.xliff
78
79
  - spec/spec_helper.rb