-
Notifications
You must be signed in to change notification settings - Fork 24
Developing
So you want to write code in Umlaut gem itself.
First, note that you may not have to for localization. The most common thing you'd want to do is add a new Service Plugin. You can add a new service plugin solely in your local app, and reference it in your umlaut_services.yml. However, if it's possibly useful to someone else, it would be great if you contributed it back to Umlaut, and that would require editing Umlaut source.
Or you may just want to explore the source and maybe even edit it experimentally, that's cool.
(Please note: You can edit Umlaut code to contribute a patch back to Umlaut, or experimentally. But you should not need to run your application live against locally edited Umlaut source code. If you do so, you have locally forked Umlaut, and it will make upgrading to future versions of Umlaut difficult. It should be possible to make any local changes you need with local configuration or over-rides located in your local app. If there's something that needs to be easier... well, that's indeed a reason to fix Umlaut to make it easier and contribute your fix back to Umlaut!)
So ordinarily when you install Umlaut into a local app, the actual Umlaut source is off in your system (or rvm, etc) gem repository, which is not a place you'd want to edit it.
You can use git to check out a copy of Umlaut source wherever you want from your github repo. But Umlaut's a gem, you can't actually run it to test it. How do you run an application against an arbitrary local umlaut version in your file system?
Two ways:
-
In any arbitrary umlaut-using local app, you can simply point it to a local checkout of Umlaut in the app's Gemfile, using the :path option. Relative paths are fine.
gem 'umlaut', :path => '../umlaut'
You wouldn't want to actually deploy or run an app in production like this, it's best to deploy against actual released versions of Umlaut. But for testing and experimentation and development.
-
The umlaut source comes with a very basic dummy app in
./test/dummy
. There's no database.yml though. Create one, pointing to a local mysql database, runrake db:migrate
, and you can just start the dummy app running against the checkout of umlaut in the directory that contains it. Edit the./config/umlaut_services.yml
if you want, etc.This dummy app was actually created by the
rails plugin new
generator for purpose of running automated tests against, see below.
See also Writing a Service Plugin
- Please, straight javascript, no coffeescript.
- scss is great though
Sorry, Umlaut doesn't contain much in the way of automated testing.
This situation comes about through a combination of: Umlaut's long history, including beginnings with no tests and many major architectural (such as through Rails versions since 1.x!) that broke the tests that were there; general developer laziness; and some genuine difficulty in figuring out how to easily test something like Umlaut that relies on so many localized external services (Umlaut doesn't do much at all by itself, it just calls out to various things that are local or require private API keys).
Nevertheless, we'd love to have some better test coverage. So we can't say that tests are really required for commits, but they're definitely a good idea.
We used rails 3.1 rails plugin new
generator to create a gem plugin skeleton for Umlaut.
One thing this did was create a ./test/dummy
dummy app in umlaut source.
This is meant for running tests against. Tests you write in /test/*
will run against the dummy app. We've manually upgraded this dummy app from Rails 3.1 to current latest version tested against.
The dummy app can have configuration customized (with those changes committed) if needed to exercise certain features
(although changing config 'live' and temporarily in a test itself would be preferable where feasible).
The dummy app doesn't have a database.yml
, you'll need to add one pointing at a local mysql
(not sqlite3, umlaut won't work against sqlite3).
We only have one dummy app in source, however by loading different versions of Rails in the gemfile, we can try to run tests against different versions of Rails. We have a line in the local Gemfile that looks at a RAILS_GEM_SPEC env variable, and if found will insist on that version of Rails. This is somewhat experimental and clunky, but you can try:
$ RAILS_GEM_SPEC="~> 3.2.0" bundle update
$ RAILS_GEM_SPEC="~> 3.2.0" bundle exec rake test
When you are done, you'll probably want to restore your Gemfile.lock
to latest version of Rails allowed with $ bundle update
(no RAILS_GEM_SPEC env var).
We do not currently run against multiple versions of Rails in travis CI, only against the latest allowed with Umlaut.
Please use straight Test::Unit / Minitest. Please do not use rspec, cucumber, etc. We are interested in moving toward minitest/spec style in the future -- but still with assert_*
style individual tests.
Put tests in ./test
. There's already a rake test
to run all tests.
Tests should pass for everyone with a straight umlaut checkout without modifying any files but adding a database.yml. To run tests, call bundle exec rake test
from the test directory. Note that some Umlaut users have experienced problems running tests on a Windows machine. In this case, try moving the line gem 'mysql2', "~> 0.3.11"
outside the block platforms :ruby do...
and re-running the tests.
Please don't add tests that raise or fail without a private API key or access to a private server.
We've started to use the VCR gem to record HTTP responses to provide deterministic testing results.
VCR "cassettes" are committed to the repo in ./test/vcr_cassettes/<module>
.
To run VCR tests, you can leverage the TestWithCassette
support module following the example below.
class GoogleBookSearchTest < ActiveSupport::TestCase
extend TestWithCassette
# Use VCR to provide a deterministic GBS search.
test_with_cassette("frankenstein by OCLC number", :google_book_search) do
hashified_response = @gbs_default.do_query('OCLC2364071', requests(:frankenstein))
assert_not_nil hashified_response
assert_not_nil hashified_response["totalItems"]
assert_operator hashified_response["totalItems"], :>, 0
end
end
We still want to figure out a way for someone with access to the necessary third party platforms to choose to run against 'live' too (for only specified services?) (and then commit the new versions of the responses back to repo if they want). There's some stuff to figure out, yeah.
VCR offers a configuration method to filter sensitive data. Since Umlaut configures VCR in test_helper, you'll want to add your filter there.
Then you need to deal with your sensitive data. One way to handle this, well, sensitively is via environment variables. Instead of having to edit your sensitive data in/out of source before committing, just stick it in your environment, and have the test do
token = ENV['SOME_SERVICE_TOKEN'] || "DUMMY_TOKEN"
# Awesome tests using the token
...
and have your test_helper filter do
VCR.configure do |c|
...
c.filter_sensitive_data("DUMMY_TOKEN") { ENV['SOME_SERVICE_TOKEN'] }
end
Then set your token in your environment
export SOME_SERVICE_TOKEN="your real token"
Now you can run your tests, and it picks up the password from your shell environment, but it never needs to be in source, so no chance of forgetting to edit it out, AND no need to be always going and editing it.
If you really must write tests that require private api keys or access to private servers to pass, then I guess that's better than no tests at all, but figure out a way to:
- Clearly document where the developer should put those private api keys or local server URLs to get tests to pass.
- Keep from interfering with running the rest of the test suite for a developer who does not have that private information filled out. Ideally tests that require it would output a clear 'skipped' or 'pending' message (explaining what needs to be configured to run them_ rather than failing.) In no cases should they raise exceptions in such a way that prevents the rest of the test suite from continuing.
Travis is a continuous integration service for the open source community that Umlaut leverages to run tests. One reason to use Test::Unit/Minitest is that Travis will automatically run your tests.
Travis creates the necessary MySQL databases and runs tests against the dummy app based on the database configuration in
./test/dummy/config/travis_database.yml
.
One specific test Travis runs is for SFX search functionality.
Travis uses the configuration in ./test/dummy/config/travis_database.yml
to create a mock instance of an
SFX global and local database, migrates the SFX database schema and the test populates the database with
deterministic SFX Object and AZ Title fixtures.
It tests basic search functionality and serves as a sanity check to make sure SFX searching is working as expected.
This test make @jrochkind very nervous, and you probably shouldn't ever run this test locally. The migrations and test code
only run in the case that the SFX database configuration is specified as a 'mock instance' in order to prevent overwriting
a real SFX database. (Yet another reason the Umlaut user that queries SFX should only have read permissions.)
Umlaut comes with a bunch of service plugins included.
You/we could instead release one or more service plugins as seperate gems, that depend on Umlaut. A user would need to install the umlaut_crazy_service gem, and then they'd have access to service plugin same as anything else. No special magic, the seperate gem just needs to depend on umlaut in it's gemspec, and include a class defining a service per Umlaut conventions.
Why might you want to do this?
- The service has a lot of dependencies that we don't want core umlaut to need to depend on
- You want to be able to release updates to the service plugin on it's own release schedule, not tied to updates of umlaut.
- You want to control the service yourself and do whatever you want with it, not subject to other umlaut developers ideas. Or you want/need to release it under a different license.
What are the downsides?
- A bit more apparatus to work with as a developer, a bit more confusing as a developer (not so much as a user), a bit more work to make sure the independent gem keeps working with Umlaut as umlaut evolves, keep track of what versions of Umlaut your independent gem works with, etc.
So you're not an Umlaut committer, but you'd like to submit a change back to Umlaut.
-
Fork in github, create a new "feature" branch in your forked github, and commit your changes there.
-
Then submit a github pull request back to Umlaut. See also http://www.mikeball.us/blog/how-to-contribute-to-a-project-hosted-on-github
-
Small single purpose pull requests are better than massive conglomerations. If you need massive changes, try to break them into steps and submit pull requests for smaller changes at a time.
-
Please feel free to ask for feedback on the umlaut listserv on your general approach before embarking on a massive change, to make sure you're working in a direction likely to be accepted.
Once you've contributed a quality change or two to Umlaut, odds are we'll make you a committer.
Generally only done by project lead or what have you. Needs to have permissions set on rubygems for the umlaut
gem.
We use the bundler-supplied rake tasks for releasing the gem. Update ./lib/umlaut/version.rb
to desired version.
Then simply run: rake release
This will create a tag with version in umlaut git repo, as well as release the new version to rubygems.org.