We are looking at migrating our build system from Ant+Ivy to something else, and Buildr is one of the possibilities. However, it doesn't appear to support Ivy out of the box, and we have no desire to convert our in-house Ivy repo and ivy.xml files to Maven and POMs.
I see there is an ivy4r project with a Buildr extension which appears to be the only way to integrate the two. However, the project hasn't had new development in quite some time, and there are no solid examples or documentation.
Does anyone have a sample of Buildr+Ivy integration or a simple example of ivy4r? I'm not a Ruby developer so the syntax is foreign to me, and without some sample code I'm afraid it will be very difficult to make this work.
The following is a small buildfile that shows buildr together with ivy4r. The comments should make it clear what happens.
The most important thing to remember is that the post_resolve blocks are only executed while the build is already running. If you set up your task dependencies that cause ivy resolving in their, it is too late.
require 'buildr/ivy_extension'
repositories.remote << "http://repo1.maven.org/maven2"
THIS_VERSION = 1.0
define 'my-app', :version => THIS_VERSION do
# Tell ivy4r to add task dependencies to the compile and test tasks.
# These tasks will cause ivy to resolve the needed artefacts.
ivy.compile :conf => 'default', :type => 'jar'
ivy.test :conf => 'test', :type => 'jar'
# Declare package tasks so that buildr sets up their dependencies.
jar = package :jar
zip = package :zip
# During the build, at some point ivy will resolve because of the
# task dependencies set up above.
# After ivy has resolved, the post_resolve blocks will be run.
ivy.post_resolve do |ivy|
# Now that ivy has resolved, get a list of the jar artefacts.
deps = ivy.deps :conf => 'default', :type => 'jar'
# Do something interesting with the artefacts.
# -> e.g. put them in the manifest of the main jar
jar.with :manifest => manifest.merge({
'Main-Class' => 'Main',
'Class-Path' => deps.map { |f| "#{File.basename(f)}" }.join(', '),
})
# -> package the app as a zip, including also those artefacts
# This results in a main.jar that specifies its own class path
# and can be run by double-clicking or just java -jar main.jar
zip.path("#{id}-#{version}").tap do |p|
p.include jar, :as => 'main.jar'
p.include deps
end
end
end
Related
What I am trying to accomplish:
I want the tomcat 9 server to execute an automatic redeploy when I run a Gradle task that updates my HTML files.
This is my setup:
I use IntelliJ 2020.03 (ultimate edition), tomcat 9, the application is a GWT application.
My Configuration for Tomcat:
This is what I see when I hit the "Configure ..." button next to the line labeled "Application server:"
This is my Gradle task I run but tomcat does not automatically redeploy the changes
Things I already tried:
According to [https://www.jetbrains.com/help/idea/updating-applications-on-application-servers.html] I should have an option to "Update resources". But my options are limited to:
Therefore I assume I need to have "Exploded artifacts in local application server run configurations".
Therefore I headed to Tomcat -> Edit Configuration
I replaced the deploy to the exploded artifact:
Using this I get the following error message on starting up tomcat:
[2021-02-12 08:46:05,533] Artifact Gradle : NewComApp.war (exploded): com.intellij.javaee.oss.admin.jmx.JmxAdminException: com.intellij.execution.ExecutionException: C:\Users\heckner\IdeaProjects\NewComApp\build\libs\exploded\NewComApp.war not found for the web module.
So I decided to compare the artifact that "works" (but does not update the HTML files) with the "exploded" artifact which would be probably the right one but throws an error message on startup of tomcat.
This is the one which works ("NewComWar.war"):
This is the one which does throw an error message on startup ("NewComApp.war (exploded)":
As you can see in the image under "... which works". the war already seems to be "exploded". So why does IntelliJ does not offer the "update resources"?
But never the less, when I switch in Tomcat Edition to "NewCompApp.war (exploded)" i am able to select "update resources" in the drop down:
So probably this would be the way to go.
It obviously boils down to the point: What is wrong with the artifact declaration above so that tomcat throws the error message?
The feedback was: "ctually "NewComWar.war" is an archive that contains exploded artifact, that's why only "Redeploy" is possible. Please check that exploded artifact is created in "Output directory". "
Now the question is how I can add the exploded war to the Output Directory?
I tried:
but then I can only select from:
When I add this, it looks like this:
When I run Tomcat, it still says:
[2021-02-12 12:24:54,224] Artifact Gradle : NewComApp.war (exploded): com.intellij.javaee.oss.admin.jmx.JmxAdminException: com.intellij.execution.ExecutionException: C:\Users\heckner\IdeaProjects\NewComApp\build\libs\exploded\NewComApp.war not found for the web module.
Now I found the following tip (thanks Evgeny):
https://youtrack.jetbrains.com/issue/IDEA-178450#focus=streamItem-27-4068591.0-0
I switched under Settings -> Build, Execution, Deployment -> Build Tools -> Gradle: "Build and Run:" IntelliJ IDEA
I added this snipped to build.gradle:
task explodedWar(type: Copy) {
into "$buildDir/libs/exploded/${war.archiveFileName.get()}"
with war
}
war.dependsOn explodedWar
I switched the artifact which is deployed to the tomcat to
this automatically added the Gradle task:
Build 'Gradle:NewComApp.war (exploded) artifact to the
which is defined like this:
This accomplishes two things:
I can choose "Update resources" on my Edit Configuration for Tomcat like shown below:
My deployment runs well under tomcat
But ... :-)
Updates to the HTML files (within the war file) are not exploded to the NewComWar.war directory.
When I start tomcat I see the following file structure under C:\users<myname>\IdeaProjects\NewComApp\Libs\
The reason for this is that we use a Gradle task that generates the HTML files.
This task is called "copyHTML"
Under build.gradle it is defined now as follows:
war {
from 'war'
dependsOn copyHtml
exclude excludeInWar
doFirst {
manifest {
def version = ant.hasProperty('gitversion') ? ant.gitversion : 'undefined version'
println "Version: ${version}"
attributes("Implementation-Title": project.name, "Implementation-Version": version, "Built-By": new Date())
}
}
}
task explodedWar(type: Copy) {
into "$buildDir/libs/exploded/${war.archiveFileName.get()}"
with war
}
war.dependsOn explodedWar
copyHtml {
dependsOn generatorClasses
inputs.dir 'html'
inputs.dir 'email'
inputs.dir 'email.Tags'
inputs.dir props.getProperty('generator.htmlfiles.prefix') + 'html'
inputs.dir props.getProperty('generator.htmlfiles.prefix') + 'html.MeetingApp'
inputs.dir props.getProperty('generator.htmlfiles.prefix') + 'staticHtml'
inputs.properties props
outputs.dirs 'war', 'resources/com/newcomapp/server/mail'
doFirst {
ant.properties["generator.classpath"] = sourceSets.generator.runtimeClasspath.getAsPath()
}
}
task warWithoutGwt(type: War, dependsOn: war) {
}
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(warWithoutGwt)) {
compileGwt.enabled = false
}
}
When I run the Gradle task "warWithoutGWT" while tomcat still runs it says:
C:\Users<myname>\IdeaProjects\NewComApp\build\libs\exploded\NewComApp.war\WEB-INF\classes\com\newcomapp\server\integration\GeoLite2-Country.mmdb (The operation is not applicable to a file with an open area assigned to a user)
I assume that tomcat still holds a reference to that file, and the Gradle task tries to overwrite it (although there was no change to that file). Furthermore, I assume that this kills the rest of the Gradle task so that it does not update the HTML files (it's only an assumption though). How can I arrange an exploded war so that write-protected files are omitted and do not kill the rest of the Gradle task execution?
My answer up to now for this problem is: I changed the gradle script:
task explodedWar(type: Copy) {
into "$buildDir/libs/exploded/${war.archiveFileName.get()}"
exclude "**/*.mmdb"
with war
}
war.dependsOn explodedWar
so I added an "exclude for mmdb files". And this really works.
Is this a correct and good solution or do I overlook something? The reason I am asking is that changing HTML files in the scope of tomcat should be something very common with tomcat based projects. So I wonder if there is a more standardized, easier solution to this? It seems quite clumsy to copy and explode with additional gradle tasks the war file instead of IDEA take care of this.
Like the title says, it detects changes in spec folder, not in lib folder.
Guardfile
guard :rspec, cmd: "bundle exec rspec" do
require "guard/rspec/dsl"
dsl = Guard::RSpec::Dsl.new(self)
# Feel free to open issues for suggestions and improvements
# RSpec files
rspec = dsl.rspec
watch(rspec.spec_helper) { rspec.spec_dir }
watch(rspec.spec_support) { rspec.spec_dir }
watch(rspec.spec_files)
# Ruby files
ruby = dsl.ruby
files = ruby.lib_files
save_path = '/data/sites/scripts/sw_scripts/ruby/files.txt'
File.write(save_path, ruby.lib_files.inspect)
dsl.watch_spec_files_for(ruby.lib_files)
end
Gemfile
source 'https://rubygems.org'
gem 'nokogiri'
gem 'thor'
gem 'rmagick'
group :development do
gem 'guard-rspec', require: false
gem 'simplecov', :require => false
end
group :development, :test do
gem 'pry-byebug'
gem 'rspec', ">=3.0"
end
What I did:
read lots of Guard documentation - it's hopelessly outdated, Guardfile has changed it's syntax (it contains some regexes while there was no regexes in my autogenerated Guardfile, links to 3+ year old tutorials etc)
put puts statement in Guardfile to see what is ruby.lib_files
it didn't work so I used File.write(path, ruby.lib_files.inspect)
file contained regexp
So I went into irb and confirmed that regexp is correct, it matches all lib files from Dir['**/*']
r = /^(lib\/.+)\.rb$/
`pwd`
=> "correct_dir\n"
files = Dir['**/*']
=> ** 149 files **
puts files.select { |f| r =~ f }
=> ** 36 files (snippet) **
lib/gallery_util/gallery_source.rb
lib/gallery_util/legacy_gallery_updater.rb
lib/gallery/specs/specs.rb
lib/gallery/specs/report.rb
lib/gallery/specs/universal_property.rb
lib/gallery/front_page_creator.rb
...
So all files are recognized, but when I run guard for any of the lib files it doesn't work at all. I get this output when I run guard in debug mode:
09:55:44 - DEBUG - Interactor was stopped or killed
09:55:44 - DEBUG - Hook :run_on_modifications_begin executed for Guard::RSpec
09:55:44 - DEBUG - Hook :run_on_modifications_end executed for Guard::RSpec
09:55:44 - DEBUG - Start interactor
Only thing that comes to mind is that guard for some reason doesn't look into lib folder recursively. I doubt that possible, why would anyone decide that?
Other thing that it depends on location of .git.
.git is 2 folders above this one (because this is collection of independent scripts that work togather and I want them in 1 repo, not 5+)
How to fix it? How to force guard to watch lib folder recursively?
It seems that guard needs spec folders structure to match identically the lib folder structure. It can't figure out matching spec if you have files
lib/gallery/front_page.rb
spec/front_page_spec.rb
No you have to create it like this:
lib/gallery/front_page.rb
spec/lib/gallery/front_page_spec.rb
I assume this would work as well (but haven't tried it)
lib/gallery/front_page.rb
spec/gallery/front_page_spec.rb
To guard maintainers: surely would be nice if that was documented somewhere.
I am new to Guard and spent the last two days to understand why RSpec doesn't start when a 'something_test.rb' file is modified and saved.
Digging deep into the code, I have found a method in Inspectors::BaseInspector
def _select_only_spec_files(paths)
spec_files = _collect_files("*[_.]spec.rb")
which forces test files to be named '..._spec.rb'. According to the documentation of Guard/Dsl :
The watch method tells Guard which changed files the plugin should respond to.
In my opinion this hard-coding of '_spec.rb' is in contradiction with the freedom that watch gives to you.
Also I don't like the code in your Guardfile, which is generated by guard init rspec. The intention of the authors is certainly good, it allows newcomers to quickly start automating RSpec tests. But one don't master what's going on behind the scene, and it seems to be your case.
This is my Guardfile with a patch to redefine SimpleInspector#paths :
directories %w[ lib spec ]
guard :rspec, cmd: 'rspec' do
require 'guard/rspec'
require "guard/rspec/inspectors/simple_inspector.rb"
module ::Guard
class RSpec < Plugin
module Inspectors
class SimpleInspector < BaseInspector
def paths(paths)
# please don't clear modified files correctly detected
# by watch but whose name doesn't end with '_spec.rb'
paths # return input without modification
end
end
end
end
end
# test files
watch(/^spec\/.*_test\.rb$/)
# source files
# watch(%r|^lib/(?<path>.+)\.rb$|) { | m | "spec/#{m[:path]}_test.rb" }
# *** same as ***
watch(/^lib\/(.+)\.rb$/) { | m | "spec/#{m[1]}_test.rb" }
end
This is a temporary workaround I hope, at least it lets me define a watch statement the way I want, which corresponds to my existing test files.
The first watch detects changed RSpec '..._test.rb' files in the spec directory. The second watch detects a changed source file in the lib directory or one of its subdirectories, and transforms its name in the block so that RSpec will be presented a 'spec/..._test.rb' file.
This way you get rid of any constraint and can master your own regexp. You could even manipulate the path in the block, if the hierarchy of the tests doesn't match the one of the sources : why not
watch(/^abd\/def\/(.+)\.rb$/) { | m | "anywhere\/you\/like\/#{m[1]}_test.rb" }
And I can reassure you : guard-2.14.1 on my Mac perfectly detects changed files even deep inside the lib folder hierarchy.
HTH
PS : I don't have bundle, but you can replace cmd: 'rspec' by cmd: "bundle exec rspec" in the guard command.
Some console output to convince you it works (the test file is empty) :
21:08:29 - DEBUG - Start interactor
21:08:29 - DEBUG - Command execution: stty -g 2>/dev/null
[1] guard(main)> _[Guard] in guard.rb #async_queue_add ch={:modified=>["lib/antlr4/ruby_static/listeners/please_check_me.rb"], :added=>[], :removed=>[]}
21:08:36 - DEBUG - Interactor was stopped or killed
21:08:36 - DEBUG - Command execution: stty 2>/dev/null
_[Guard] //1 in Runner#run_on_changes mod=["lib/antlr4/ruby_static/listeners/please_check_me.rb"] add=[] rem=[]
...
_[Guard] //3 in Runner#run_on_changes ... plugin Guard::RSpec yielded, types={[:run_on_modifications, :run_on_changes, :run_on_change]=>["lib/antlr4/ruby_static/listeners/please_check_me.rb"], [:run_on_additions, :run_on_changes, :run_on_change]=>[], ...
_[Guard] //3+ plugin=#<Guard::RSpec #name=rspec #group=#<Guard::Group #name=default #options={}> #watchers=[#<Guard::Watcher:0x0..., #pattern=#<Guard::Watcher::Pattern::Matcher:0x007fbdca22d780 #matcher=/^spec\/.*_test\.rb$/>>, ..., #pattern=#<Guard::Watcher::Pattern::Matcher:0x007fbdca22d528 #matcher=/^lib\/(.+)\.rb$/>>]
...
_[Guard] //5 in Runner#run_on_changes ... match_result=["spec/antlr4/ruby_static/listeners/please_check_me_test.rb"]
...
task=run_on_modifications
_[Guard] /+-+ in Runner#_supervise pl=Guard::RSpec, t=run_on_modifications, args=[["spec/antlr4/ruby_static/listeners/please_check_me_test.rb"]]
...
21:08:36 - DEBUG - Hook :run_on_modifications_begin executed for Guard::RSpec
_[Guard] §§§ in Plugin self.notify gp=Guard::RSpec, ev=run_on_modifications_begin, args=[["spec/antlr4/ruby_static/listeners/please_check_me_test.rb"]]
_[Guard] /+-+ in Runner#_supervise about to plugin.send task=run_on_modifications, args=[["spec/antlr4/ruby_static/listeners/please_check_me_test.rb"]]
_[RSpec] &&&& in RSpec#run_on_modifications paths=["spec/antlr4/ruby_static/listeners/please_check_me_test.rb"]
_[RSpec] &&&& in RSpec#run_on_modifications about to runner.run
_[RSpec] ))) in RSpec::Runner#run paths=["spec/antlr4/ruby_static/listeners/please_check_me_test.rb"] inspector=#<Guard::RSpec::Inspectors::SimpleInspector:0x007fbdca2351b0>
_[RSpec] ))) in RSpec::Runner#run after inspector ["spec/antlr4/ruby_static/listeners/please_check_me_test.rb"]
21:08:36 - INFO - Running: spec/antlr4/ruby_static/listeners/please_check_me_test.rb
_[RSpec] ))) in RSpec::Runner#_run
_[RSpec] ))) in RSpec::Runner#_run command=rspec
...
_[RSpec] ))) in RSpec::Runner#_really_run cmd=rspec -f documentation -r /Users/b/.rvm/gems/ruby-2.3.3#rspec2/gems/guard-rspec-4.7.3/lib/guard/rspec_formatter.rb ... spec/antlr4/ruby_static/listeners/please_check_me_test.rb
...
21:08:36 - DEBUG - Command execution: ...
Finished in 0.00042 seconds (files took 0.10208 seconds to load)
0 examples, 0 failures
...
21:08:36 - DEBUG - Guard::RSpec: RSpec command spec ... exited with: 0
...
21:08:36 - DEBUG - Start interactor
21:08:36 - DEBUG - Command execution: stty -g 2>/dev/null
Problem
If variables can be passed to a Puppet class, e.g.:
class module_name (
$variable='hello_world'
) {
package { 'package_name': }
}
and rspec is run it fails, i.e.:
[user#host module_name]$ rspec
...............................FFFFFFFFFFFF..........................................
Failures:
1) opsview should contain Class[module_name]
Failure/Error: it { should contain_class('module_name') }
Puppet::Error:
Error from DataBinding 'hiera' while looking up 'module_name::variable':
FileSystem implementation expected Pathname, got: 'Hash' on node host
# /usr/share/ruby/vendor_ruby/puppet/resource.rb:393:
in `rescue in lookup_with_databinding'
# /usr/share/ruby/vendor_ruby/puppet/resource.rb:387:
in `lookup_with_databinding'
# /usr/share/ruby/vendor_ruby/puppet/resource.rb:381:
in `lookup_external_default_for'
Main issue
Error from DataBinding while looking up FileSystem implementation expected Pathname,
got: 'Hash' on node
Configuration
Versions
[vagrant#vm-one opsview]$ puppet --version
3.7.5
[vagrant#vm-one opsview]$ rspec --version
3.2.2
Spec_helper
[vagrant#vm-one opsview]$ cat spec/spec_helper.rb
require 'rspec-puppet'
require 'hiera-puppet-helper'
fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
RSpec.configure do |c|
c.module_path = File.join(fixture_path, 'modules')
c.manifest_dir = File.join(fixture_path, 'manifests')
c.hiera_config = '/etc/puppet/hiera.yaml'
end
at_exit { RSpec::Puppet::Coverage.report! }
Attempt
According to this Q&A hiera-puppet-helper is causing the
issue. Rspec-puppet seems to support testing of hiera and
'hiera-puppet-helper' could be replaced. Ok perhaps this solves the issue, but what is causing the issue?
This post contains the same issue, but not a solution to solve
the issue
This post indicates that removing the class parameters solves the issue, but this class is used by multiple modules and therefore this is not a solution.
There's a problem with the hiera_config, it should be:
c.hiera_config = 'spec/fixtures/hiera/hiera.yaml'
assuming that you have directory structure like:
\spec
\fixtures
\hiera
hiera.yaml
default.yaml
and hiera.yaml:
---
:backends:
- yaml
:hierarchy:
- '%{::clientcert}'
- default
:yaml:
:datadir: './spec/fixtures/hiera'
The hiera-puppet-helper gem is indeed not required, in fact it shouldn't be there. I'd recommend generating module structure using Garethr's skeleton which already contains working setup for Hiera and can save you a lot of time.
I am new to Puppet and want to install jetty, through the module that is already available here Link
For this I installed the jetty module using this:
puppet module install maestrodev-jetty
Now I have this file structure
puppet
== modules
==== jetty
== manifests
==== site.pp
==== classes
== gg_jetty.pp
the gg_jetty.pp has the following content :
class gg_jetty{
include jetty
jetty{
version => "9.0.4.v20130625",
home => "/opt",
user => "jetty",
group => "jetty",
}
}
the site.pp has the following content :
import classes/*.pp
node default{}
node 'pajetty1'{include gg_jetty}
Now when I run the command like this in 'pajetty1' using :
puppet agent --no-daemonize --verbose --onetime
It shows me following error:
What I am doing wrong, (also for viewing the pic please right click the image and open in new tab)
Thanks in advance..
´jetty{´ needs a name:
class gg_jetty{
include jetty
jetty{'foo':
version => "9.0.4.v20130625",
home => "/opt",
user => "jetty",
group => "jetty",
}
}
So catalog load fails, and puppet uses cached catalog
By other side, it seem's that your version (9.0.4.v2013062 , with last 5 missing) is not accesible through your yum repos. Type yum info jetty to see what versions have you available through yum, and change either repos or recipes to make them match.
It is pretty easy with the added generator of rspec-rails to set up RSpec for testing a Rails application. But how about adding RSpec for testing a gem in development?
I am not using jeweler or such tools. I just used Bundler (bundle gem my_gem) to setup the structure for the new gem and edit the *.gemspec manually.
I also added s.add_development_dependency "rspec", ">= 2.0.0" to gemspec and did a bundle install.
Is there some nice tutorial what to do next to get RSpec working?
I've updated this answer to match current best practices:
Bundler supports gem development perfectly. If you are creating a gem, the only thing you need to have in your Gemfile is the following:
source "https://rubygems.org"
gemspec
This tells Bundler to look inside your gemspec file for the dependencies when you run bundle install.
Next up, make sure that RSpec is a development dependency of your gem. Edit the gemspec so it reads:
spec.add_development_dependency "rspec"
Next, create spec/spec_helper.rb and add something like:
require 'bundler/setup'
Bundler.setup
require 'your_gem_name' # and any other gems you need
RSpec.configure do |config|
# some (optional) config here
end
The first two lines tell Bundler to load only the gems inside your gemspec. When you install your own gem on your own machine, this will force your specs to use your current code, not the version you have installed separately.
Create a spec, for example spec/foobar_spec.rb:
require 'spec_helper'
describe Foobar do
pending "write it"
end
Optional: add a .rspec file for default options and put it in your gem's root path:
--color
--format documentation
Finally: run the specs:
$ rspec spec/foobar_spec.rb
Iain's solution above works great!
If you also want a Rakefile, this is all you need:
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
# If you want to make this the default task
task default: :spec
Check the RDoc for RakeTask for various options that you can optionally pass into the task definition.
You can generate your new gem with rspec by running bundler gem --test=rspec my_gem. No additional Setup!
I always forget this. It's implemented here: https://github.com/bundler/bundler/blob/33d2f67d56fe8bf00b0189c26125d27527ef1516/lib/bundler/cli/gem.rb#L36
Here's a cheap and easy (though not officially recommended) way:
Make a dir in your gem's root called spec, put your specs in there. You probably already have rspec installed, but if you don't, just do a gem install rspec and forget Gemfiles and bundler.
Next, you'll make a spec, and you need to tell it where your app is, where your files are, and include the file you want to test (along with any dependencies it has):
# spec/awesome_gem/awesome.rb
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
$: << File.join(APP_ROOT, 'lib/awesome_gem') # so rspec knows where your file could be
require 'some_file_in_the_above_dir' # this loads the class you want to test
describe AwesomeGem::Awesome do
before do
#dog = AwesomeGem::Awesome.new(name: 'woofer!')
end
it 'should have a name' do
#dog.name.should eq 'woofer!'
end
context '#lick_things' do
it 'should return the dog\'s name in a string' do
#dog.lick_things.should include 'woofer!:'
end
end
end
Open up Terminal and run rspec:
~/awesome_gem $ rspec
..
Finished in 0.56 seconds
2 examples, 0 failures
If you want some .rspec options love, go make a .rspec file and put it in your gem's root path. Mine looks like this:
# .rspec
--format documentation --color --debug --fail-fast
Easy, fast, neat!
I like this because you don't have to add any dependencies to your project at all, and the whole thing remains very fast. bundle exec slows things down a little, which is what you'd have to do to make sure you're using the same version of rspec all the time. That 0.56 seconds it took to run two tests was 99% taken up by the time it took my computer to load up rspec. Running hundreds of specs should be extremely fast. The only issue you could run into that I'm aware of is if you change versions of rspec and the new version isn't backwards compatible with some function you used in your test, you might have to re-write some tests.
This is nice if you are doing one-off specs or have some good reason to NOT include rspec in your gemspec, however it's not very good for enabling sharing or enforcing compatibility.