enki 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8523f2685c2123bf425f7d19aa4e30c281c838ca
4
+ data.tar.gz: ff57940d741e8c08feb6638dedaf49bfccfb45cc
5
+ SHA512:
6
+ metadata.gz: 7622883a70c067430fb391415c0ef8763021e4dc9f3d4be3a0027ddfced4242059c7986f2e21e1cb5231216f209d47e577ea455b591c919ce59e1893efe58d6e
7
+ data.tar.gz: 6e3c3ffb6b9ac187e8df743c2da91c33f583d5f2227e7cffc61b5316b42a51cb9e31e5d76f58bcf9c7cd042a31a8f8af7956d23bca5f32b18c0df6b02b5fba39
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in enki.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Giorgos Avramidis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Enki
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'enki'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install enki
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/enki/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Open an irb session preloaded with this library"
4
+ task :console do
5
+ sh "irb -rubygems -I lib -r enki.rb"
6
+ end
7
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'enki/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "enki"
8
+ spec.version = Enki::VERSION
9
+ spec.authors = ["Giorgos Avramidis"]
10
+ spec.email = ["avramidg@gmail.com"]
11
+ spec.summary = %q{Provide snowcrash and confluence integration}
12
+ spec.description = %q{}
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_runtime_dependency "qarioz-confluencer", "~> 0.6", '>= 0.6.0'
22
+ spec.add_runtime_dependency "redcarpet", "~> 3.1", ">= 3.1.2"
23
+ spec.add_development_dependency "bundler", "~> 1.6", ">= 1.6.0"
24
+ spec.add_development_dependency "rake", "~> 10.3", ">= 10.3.2"
25
+ spec.add_development_dependency "rspec", "~> 3.0", ">= 3.0.0"
26
+ end
@@ -0,0 +1,46 @@
1
+ require 'enki/version'
2
+ require 'logger'
3
+ require 'erb'
4
+ require 'yaml'
5
+
6
+ module Enki
7
+ class << self
8
+ attr_writer :configuration
9
+ end
10
+
11
+ def self.logger
12
+ @logger ||= Logger.new(STDOUT)
13
+ end
14
+
15
+ def self.logger=(logger)
16
+ @logger = logger
17
+ end
18
+
19
+ def self.configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ def self.configure
24
+ yield(configuration)
25
+ end
26
+
27
+ class Configuration
28
+ attr_accessor :confluence_url,
29
+ :confluence_user,
30
+ :confluence_password,
31
+ :confluence_space
32
+
33
+ attr_accessor :snowcrash_binary
34
+ attr_accessor :erb_template
35
+
36
+ def initialize
37
+ @erb_template = File.join File.dirname(__FILE__), "templates/default.html.erb"
38
+ end
39
+ end
40
+ end
41
+
42
+ require 'enki/confluence.rb'
43
+ require 'enki/redcarpenter.rb'
44
+ require 'enki/snowcrasher.rb'
45
+
46
+ require 'enki/railtie.rb' if defined?(Rails) && Rails::VERSION::MAJOR > 2
@@ -0,0 +1,68 @@
1
+ require "confluencer"
2
+
3
+ module Enki
4
+ module Confluence
5
+ extend self
6
+
7
+ def upload(file:, title: nil, space: nil)
8
+ confluence_session do |client|
9
+ upload_with_client(file: file, client: client, title: title, space: space)
10
+ end
11
+ end
12
+
13
+ def process_dir(src_dir:)
14
+ confluence_session do |client|
15
+ Dir.glob("#{src_dir}/**/*.html").each do |file|
16
+ upload_with_client(file: file, client: client)
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def upload_with_client(file:, client:, title: nil, space: nil)
24
+ title ||= File.basename(file, '.*')
25
+ space ||= Enki.configuration.confluence_space
26
+
27
+ content = File.read(file)
28
+
29
+ old_page = begin
30
+ client.get_page(space, title)
31
+ rescue ::Confluence::Error
32
+ # Page does not exist yet
33
+ nil
34
+ end
35
+
36
+ if old_page
37
+ Enki.logger.info "Updating #{old_page["title"].inspect} in #{old_page["space"].inspect}..."
38
+ client.update_page({
39
+ "id" => old_page["id"],
40
+ "space" => old_page["space"],
41
+ "title" => old_page["title"],
42
+ "content" => content,
43
+ "version" => old_page["version"],
44
+ }, {})
45
+ else
46
+ Enki.logger.info "Creating #{title.inspect} in #{space.inspect}..."
47
+ client.store_page({
48
+ "space" => space,
49
+ "title" => title,
50
+ "content" => content,
51
+ })
52
+ end
53
+ Enki.logger.info "Done!"
54
+ end
55
+
56
+ def confluence_session
57
+ options = {
58
+ url: Enki.configuration.confluence_url,
59
+ username: Enki.configuration.confluence_user,
60
+ password: Enki.configuration.confluence_password
61
+ }
62
+
63
+ ::Confluence::Session.new(options) do |client|
64
+ yield(client)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rails'
4
+
5
+ module Enki
6
+ class Railtie < Rails::Railtie
7
+ rake_tasks do
8
+ load 'tasks/compile_docs.rake'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,44 @@
1
+ require 'redcarpet'
2
+
3
+ class EnkiRender < Redcarpet::Render::HTML
4
+ def block_code(code, language)
5
+ <<-HTML
6
+ <ac:structured-macro ac:name="code">
7
+ <ac:plain-text-body>
8
+ <![CDATA[#{code}]]>
9
+ </ac:plain-text-body>
10
+ </ac:structured-macro>
11
+ HTML
12
+ end
13
+ end
14
+
15
+ module Enki
16
+ module Redcarpenter
17
+ extend self
18
+
19
+ def compile_file(source:, output:)
20
+ @data = YAML.load_file(source)
21
+ @markdown = ::Redcarpet::Markdown.new(
22
+ EnkiRender,
23
+ no_intra_emphasis: true,
24
+ )
25
+
26
+ template_file = File.open(Enki.configuration.erb_template)
27
+
28
+ template = ERB.new template_file.read
29
+
30
+ File.open(output, 'w') do |file|
31
+ file.puts template.result(binding)
32
+ end
33
+ end
34
+
35
+ def compile_dir(src_dir:, dst_dir:)
36
+ Dir.glob("#{src_dir}/**/*.yml").each do |source_file|
37
+ output_file = "#{dst_dir}/#{source_file[%r{#{src_dir}/(.*)\.yml}, 1]}.html"
38
+ FileUtils.mkdir_p File.dirname(output_file)
39
+
40
+ compile_file(source: source_file, output: output_file)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ module Enki
2
+ module Snowcrasher
3
+ extend self
4
+
5
+ def compile_file(source:, output:, format: "yaml")
6
+ result = `#{Enki.configuration.snowcrash_binary} --output '#{output}' --format #{format} '#{source}' 2>&1`
7
+
8
+ unless $?.success?
9
+ raise "Snowcrasher: Error: #{result}"
10
+ end
11
+ end
12
+
13
+ def compile_dir(src_dir:, dst_dir:)
14
+ Dir.glob("#{src_dir}/**/*.md").each do |source_file|
15
+ output_file = "#{dst_dir}/#{source_file[%r{#{src_dir}/(.*)\.md}, 1]}.yml"
16
+ FileUtils.mkdir_p File.dirname(output_file)
17
+
18
+ compile_file(source: source_file, output: output_file)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module Enki
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,16 @@
1
+ namespace :enki do
2
+ desc "Generate AST files"
3
+ task :generate_ast => :environment do
4
+ Enki::Snowcrasher.compile_dir(src_dir: "doc", dst_dir: "tmp/enki/ast")
5
+ end
6
+
7
+ desc "Generate html from AST"
8
+ task :ast_to_html => :generate_ast do
9
+ Enki::Redcarpenter.compile_dir(src_dir: "tmp/enki/ast", dst_dir: "tmp/enki/html")
10
+ end
11
+
12
+ desc "Upload documentation to confluence"
13
+ task :upload_to_confluence => :ast_to_html do
14
+ Enki::Confluence.process_dir(src_dir: "tmp/enki/html")
15
+ end
16
+ end
@@ -0,0 +1,117 @@
1
+ <ac:structured-macro ac:name="toc">
2
+ <ac:parameter ac:name="maxLevel">3</ac:parameter>
3
+ </ac:structured-macro>
4
+
5
+ <h2><%= @data['name'] %></h2>
6
+ <%=@markdown.render @data['description'] %>
7
+
8
+ <% @data['resourceGroups'].each do |group| %>
9
+ <h2><%= group['name'] %></h2>
10
+ <hr/>
11
+ <% group['resources'].each do |resource| %>
12
+ <% resource['actions'].each do |action| %>
13
+ <h3><%= action['method'] %> <%= resource['uriTemplate'] %></h3>
14
+
15
+ <h5>Description</h5>
16
+ <%=@markdown.render action['description'] %>
17
+
18
+ <% if action['parameters'] %>
19
+ <h5>Parameters</h5>
20
+ <table>
21
+ <tbody>
22
+ <tr>
23
+ <th>Name</th>
24
+ <th>Type</th>
25
+ <th>Default</th>
26
+ <th>Example</th>
27
+ <th>Mandatory</th>
28
+ <th>Description</th>
29
+ </tr>
30
+ <% action['parameters'].each do |parameter| %>
31
+ <tr>
32
+ <td><%= parameter['name'] %></td>
33
+ <td><%= parameter['type'] %></td>
34
+ <td><%= parameter['default'] %></td>
35
+ <td><%= parameter['example'] %></td>
36
+ <td><%= parameter['required'] and 'yes' or 'no' %></td>
37
+ <td><%=@markdown.render parameter['description'] %></td>
38
+ </tr>
39
+ <% end %>
40
+ </tbody>
41
+ </table>
42
+ <% end %>
43
+
44
+ <% if action['body'] %>
45
+ <ac:structured-macro ac:name="code">
46
+ <ac:plain-text-body>
47
+ <![CDATA[<%= action['body'] %>]]>
48
+ </ac:plain-text-body>
49
+ </ac:structured-macro>
50
+ <% end %>
51
+
52
+ <% if action['examples'] %>
53
+ <% action['examples'].each do |example| %>
54
+ <% if example['requests'] %>
55
+ <h5>Request</h5>
56
+ <% example['requests'].each do |request| %>
57
+ <%=@markdown.render request['description'] if request['description'] %>
58
+ <% if request['body'] %>
59
+ <ac:structured-macro ac:name="code">
60
+ <ac:parameter ac:name="title">Example</ac:parameter>
61
+ <ac:plain-text-body>
62
+ <![CDATA[<%= request['body'] %>]]>
63
+ </ac:plain-text-body>
64
+ </ac:structured-macro>
65
+ <% end %>
66
+ <% if request['schema'] %>
67
+ <ac:structured-macro ac:name="code">
68
+ <ac:parameter ac:name="title">JSON Schema</ac:parameter>
69
+ <ac:parameter ac:name="collapse">true</ac:parameter>
70
+ <ac:plain-text-body>
71
+ <![CDATA[<%= request['schema'] %>]]>
72
+ </ac:plain-text-body>
73
+ </ac:structured-macro>
74
+ <% end %>
75
+ <% end %>
76
+ <% end %>
77
+ <% if example['responses'] %>
78
+ <h5>Response codes</h5>
79
+ <table>
80
+ <tbody>
81
+ <tr>
82
+ <th>Code</th>
83
+ <th>Comment</th>
84
+ </tr>
85
+ <% example['responses'].each do |response| %>
86
+ <tr>
87
+ <td><%= response['name'] %></td>
88
+ <td>
89
+ <%=@markdown.render response['description'].to_s %>
90
+ <% if response['body'] %>
91
+ <ac:structured-macro ac:name="code">
92
+ <ac:parameter ac:name="title">Example</ac:parameter>
93
+ <ac:plain-text-body>
94
+ <![CDATA[<%= response['body'] %>]]>
95
+ </ac:plain-text-body>
96
+ </ac:structured-macro>
97
+ <% end %>
98
+ <% if response['schema'] %>
99
+ <ac:structured-macro ac:name="code">
100
+ <ac:parameter ac:name="title">JSON Schema</ac:parameter>
101
+ <ac:parameter ac:name="collapse">true</ac:parameter>
102
+ <ac:plain-text-body>
103
+ <![CDATA[<%= response['schema'] %>]]>
104
+ </ac:plain-text-body>
105
+ </ac:structured-macro>
106
+ <% end %>
107
+ </td>
108
+ </tr>
109
+ <% end %>
110
+ </tbody>
111
+ </table>
112
+ <% end %>
113
+ <% end %>
114
+ <% end %>
115
+ <% end %>
116
+ <% end %>
117
+ <% end %>
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Enki do
4
+ describe "#configure" do
5
+ it "sets the confluence user" do
6
+ Enki.configure do |conf|
7
+ conf.confluence_user = "conf_user"
8
+ end
9
+
10
+ expect(Enki.configuration.confluence_user).to eql "conf_user"
11
+ end
12
+
13
+ it "sets the confluence password" do
14
+ Enki.configure do |conf|
15
+ conf.confluence_password = "conf_pass"
16
+ end
17
+
18
+ expect(Enki.configuration.confluence_password).to eql "conf_pass"
19
+ end
20
+
21
+ it "sets the confluence url" do
22
+ Enki.configure do |conf|
23
+ conf.confluence_url = "conf_url"
24
+ end
25
+
26
+ expect(Enki.configuration.confluence_url).to eql "conf_url"
27
+ end
28
+
29
+ it "sets the confluence space" do
30
+ Enki.configure do |conf|
31
+ conf.confluence_url = "conf_space"
32
+ end
33
+
34
+ expect(Enki.configuration.confluence_space).to eql "conf_space"
35
+ end
36
+
37
+ it "sets the erb template path" do
38
+ # has a default template path
39
+ expect(Enki.configuration.erb_template).to eql "lib/templates/default.html.erb"
40
+
41
+ Enki.configure do |conf|
42
+ conf.erb_template = "template_path"
43
+ end
44
+
45
+ expect(Enki.configuration.erb_template).to eql "template_path"
46
+ end
47
+ end
48
+
49
+ describe "logging" do
50
+ it "sets the logger" do
51
+ logger = Logger.new(STDOUT)
52
+ Enki.logger = logger
53
+
54
+ expect(logger).to receive(:log).with("a message")
55
+
56
+ Enki.logger.log "a message"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,4 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'enki'
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: enki
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Giorgos Avramidis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: qarioz-confluencer
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.6.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.6'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.6.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: redcarpet
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.1.2
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.1.2
53
+ - !ruby/object:Gem::Dependency
54
+ name: bundler
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.6'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.6.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.6'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.6.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: rake
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '10.3'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 10.3.2
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.3'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 10.3.2
93
+ - !ruby/object:Gem::Dependency
94
+ name: rspec
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '3.0'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 3.0.0
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 3.0.0
113
+ description: ''
114
+ email:
115
+ - avramidg@gmail.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - Gemfile
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - enki.gemspec
126
+ - lib/enki.rb
127
+ - lib/enki/confluence.rb
128
+ - lib/enki/railtie.rb
129
+ - lib/enki/redcarpenter.rb
130
+ - lib/enki/snowcrasher.rb
131
+ - lib/enki/version.rb
132
+ - lib/tasks/compile_docs.rake
133
+ - lib/templates/default.html.erb
134
+ - spec/enki_spec.rb
135
+ - spec/spec_helper.rb
136
+ homepage: ''
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.2.2
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Provide snowcrash and confluence integration
160
+ test_files:
161
+ - spec/enki_spec.rb
162
+ - spec/spec_helper.rb