Skip to content

Commit

Permalink
Updated tests and docs for upcoming gem release
Browse files Browse the repository at this point in the history
* Installation is recommended via gem
* Minimum ActiveRecord version: 2.3.x
* Setup for travis-ci.org
  • Loading branch information
boosty committed Oct 19, 2011
1 parent 3b53e85 commit 9a7842d
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 80 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -7,4 +7,6 @@ coverage
*.gem
.bundle
Gemfile.lock
gemfiles/*.lock
pkg/*
rdoc/*
7 changes: 7 additions & 0 deletions .travis.yml
@@ -0,0 +1,7 @@
rvm:
- 1.8.7
- 1.9.2
gemfile:
- gemfiles/Gemfile.activerecord-2.3.x
- gemfiles/Gemfile.activerecord-3.0.x
- gemfiles/Gemfile.activerecord-3.1.x
166 changes: 88 additions & 78 deletions README.rdoc
@@ -1,25 +1,25 @@
=FlagShihTzu

A rails plugin to store a collection of boolean attributes in a single
ActiveRecord column as a bit field.
An extension for ActiveRecord to store a collection of boolean attributes in a
single integer column as a bit field.

http://github.com/xing/flag_shih_tzu

This plugin lets you use a single integer column in an ActiveRecord model
This gem lets you use a single integer column in an ActiveRecord model
to store a collection of boolean attributes (flags). Each flag can be used
almost in the same way you would use any boolean attribute on an
ActiveRecord object.

The benefits:
* No migrations needed for new boolean attributes. This helps a lot
if you have very large db-tables where you want to avoid ALTER TABLE whenever
possible.
if you have very large db-tables, on which you want to avoid ALTER TABLE
whenever possible.
* Only the one integer column needs to be indexed.

Using FlagShihTzu, you can add new boolean attributes whenever you want,
without needing any migration. Just add a new flag to the +has_flags+ call.

And just in case you are wondering what "Shih Tzu" means:
And just in case you are wondering what a "Shih Tzu" is:
http://en.wikipedia.org/wiki/Shih_Tzu


Expand All @@ -30,27 +30,30 @@ to store the flags, which should be defined to not allow NULL values and
should have a default value of 0 (which means all flags are initially set to
false).

The plugin has been tested with Rails versions from 2.1 to 3.0 and MySQL,
PostgreSQL and SQLite3 databases. It has been tested with ruby 1.9.2 (with
Rails 3 only).
The gem is actively being tested with:

* ActiveRecord versions 2.3.x, 3.0.x, 3.1.x
* MySQL, PostgreSQL and SQLite3 databases
* Ruby 1.8.7 and 1.9.2


==Installation

===As plugin (Rails 2.x, Rails 3)
===Rails 2.x

In environment.rb:

cd path/to/your/rails-project
./script/plugin install git://github.com/xing/flag_shih_tzu.git
config.gem 'flag_shih_tzu'

===As gem (Rails 3)
Then run +rake gems:install+ (use +sudo+ if necessary).

Add following line to your Gemfile:
===Rails 3

gem 'flag_shih_tzu', '= 0.1.0.pre'
In Gemfile:

Make sure to install gem with bundler:
gem 'flag_shih_tzu'

bundle install
Then run +bundle install+.

==Usage

Expand All @@ -71,6 +74,44 @@ That is why the plugin forces you to set them explicitly.
The values are symbols for the flags being created.


===How it stores the values

As said, FlagShihTzu uses a single integer column to store the values for all
the defined flags as a bit field.

The bit position of a flag corresponds to the given key.

This way, we can use bit operators on the stored integer value to set, unset
and check individual flags.

+---+---+---+ +---+---+---+
| | | | | | | |
Bit position | 3 | 2 | 1 | | 3 | 2 | 1 |
(flag key) | | | | | | | |
+---+---+---+ +---+---+---+
| | | | | | | |
Bit value | 4 | 2 | 1 | | 4 | 2 | 1 |
| | | | | | | |
+---+---+---+ +---+---+---+
| e | s | w | | e | s | w |
| l | h | a | | l | h | a |
| e | i | r | | e | i | r |
| c | e | p | | c | e | p |
| t | l | d | | t | l | d |
| r | d | r | | r | d | r |
| o | s | i | | o | s | i |
| l | | v | | l | | v |
| y | | e | | y | | e |
| t | | | | t | | |
| e | | | | e | | |
| s | | | | s | | |
+---+---+---+ +---+---+---+
| 1 | 1 | 0 | = 4 + 2 = 6 | 1 | 0 | 1 | = 4 + 1 = 5
+---+---+---+ +---+---+---+

Read more about bit fields here: http://en.wikipedia.org/wiki/Bit_field


===Using a custom column name

The default column name to store the flags is 'flags', but you can provide a
Expand All @@ -82,7 +123,7 @@ different columns for separate flags:
3 => :electrolytes,
:column => 'features'

has_flags 1 => :spock,
has_flags 1 => :spock,
2 => :scott,
3 => :kirk,
:column => 'crew'
Expand Down Expand Up @@ -141,44 +182,6 @@ the scopes. The option on has_flags is still named <tt>:named_scopes</tt> howeve
...


===How it stores the values

As said, FlagShihTzu uses a single integer column to store the values for all
the defined flags as a bit field.

The bit position of a flag corresponds to the given key.

This way, we can use bit operators on the stored integer value to set, unset
and check individual flags.

+---+---+---+ +---+---+---+
| | | | | | | |
Bit position | 3 | 2 | 1 | | 3 | 2 | 1 |
(flag key) | | | | | | | |
+---+---+---+ +---+---+---+
| | | | | | | |
Bit value | 4 | 2 | 1 | | 4 | 2 | 1 |
| | | | | | | |
+---+---+---+ +---+---+---+
| e | s | w | | e | s | w |
| l | h | a | | l | h | a |
| e | i | r | | e | i | r |
| c | e | p | | c | e | p |
| t | l | d | | t | l | d |
| r | d | r | | r | d | r |
| o | s | i | | o | s | i |
| l | | v | | l | | v |
| y | | e | | y | | e |
| t | | | | t | | |
| e | | | | e | | |
| s | | | | s | | |
+---+---+---+ +---+---+---+
| 1 | 1 | 0 | = 4 + 2 = 6 | 1 | 0 | 1 | = 4 + 1 = 5
+---+---+---+ +---+---+---+

Read more about bit fields here: http://en.wikipedia.org/wiki/Bit_field


===Support for manually building conditions

The following class methods may support you when manually building
Expand All @@ -190,8 +193,8 @@ ActiveRecord conditions:
Spaceship.not_shields_condition # "(spaceships.flags not in (2,3,6,7))"
Spaceship.electrolytes_condition # "(spaceships.flags in (4,5,6,7))"
Spaceship.not_electrolytes_condition # "(spaceships.flags not in (4,5,6,7))"
These methods also accept a :table_alias option that can be used when

These methods also accept a :table_alias option that can be used when
generating SQL that references the same table more than once:

Spaceship.shields_condition(:table_alias => 'evil_spaceships') # "(evil_spaceships.flags in (2,3,6,7))"
Expand All @@ -209,43 +212,50 @@ For MySQL, depending on your MySQL settings, this can even hit the
In this case, consider changing the flag query mode to <tt>:bit_operator</tt>
instead of <tt>:in_list</tt>, like so:

has_flags 1 => :warpdrive,
2 => :shields,
has_flags 1 => :warpdrive,
2 => :shields,
:flag_query_mode => :bit_operator

This will modify the generated condition and named_scope methods to use bit
operators in the SQL instead of an IN() list:

Spaceship.warpdrive_condition # "(spaceships.flags & 1 = 1)",
Spaceship.not_warpdrive_condition # "(spaceships.flags & 1 = 0)",
Spaceship.shields_condition # "(spaceships.flags & 2 = 2)",
Spaceship.not_shields_condition # "(spaceships.flags & 2 = 0)",
Spaceship.warpdrive_condition # "(spaceships.flags & 1 = 1)",
Spaceship.not_warpdrive_condition # "(spaceships.flags & 1 = 0)",
Spaceship.shields_condition # "(spaceships.flags & 2 = 2)",
Spaceship.not_shields_condition # "(spaceships.flags & 2 = 0)",

Spaceship.warpdrive # :conditions => "(spaceships.flags & 1 = 1)"
Spaceship.not_warpdrive # :conditions => "(spaceships.flags & 1 = 0)"
Spaceship.shields # :conditions => "(spaceships.flags & 2 = 2)"
Spaceship.not_shields # :conditions => "(spaceships.flags & 2 = 0)"
Spaceship.warpdrive # :conditions => "(spaceships.flags & 1 = 1)"
Spaceship.not_warpdrive # :conditions => "(spaceships.flags & 1 = 0)"
Spaceship.shields # :conditions => "(spaceships.flags & 2 = 2)"
Spaceship.not_shields # :conditions => "(spaceships.flags & 2 = 0)"

The drawback is that due to the bit operator, this query can not use an index
on the flags column.


==Running the plugin tests
==Running the tests

The default rake test task will run the tests against the currently locked
ActiveRecord version (see Gemfile.lock):

$ bundle exec rake test

If you want to run the tests against all supported ActiveRecord versions:

1. (Rails 3 only) Add <tt>mysql2</tt>, <tt>pg</tt> and <tt>sqlite3</tt> gems to your Gemfile.
1. Install flag_shih_tzu as plugin inside working Rails application.
1. Modify <tt>test/database.yml</tt> to fit your test environment.
2. If needed, create the test database you configured in <tt>test/database.yml</tt>.
$ bundle exec rake test:all

Then you can run
This task will use bundler to load specific ActiveRecord versions before
executing the tests (see gemfiles/), e.g.:

DB=mysql|postgres|sqlite3 rake test:plugins PLUGIN=flag_shih_tzu
$ BUNDLE_GEMFILE='gemfiles/Gemfile.activerecord-3.1.x' bundle exec rake test

from your Rails project root or

DB=mysql|postgres|sqlite3 rake
All tests will use the sqlite3 database adapter by default.
If you want to use a different database, modify <tt>test/database.yml</tt>
to fit your environment. Then you can use the DB environment variable
to specify which config from <tt>test/database.yml</tt> to use, e.g.:

from <tt>vendor/plugins/flag_shih_tzu</tt>.
$ DB=mysql bundle exec rake


==Authors
Expand Down
8 changes: 8 additions & 0 deletions Rakefile
Expand Up @@ -25,6 +25,14 @@ RDoc::Task.new(:rdoc) do |rdoc|
end

namespace :test do
desc 'Test against all supported ActiveRecord versions'
task :all do
%w(2.3.x 3.0.x 3.1.x).each do |version|
sh "BUNDLE_GEMFILE='gemfiles/Gemfile.activerecord-#{version}' bundle"
sh "BUNDLE_GEMFILE='gemfiles/Gemfile.activerecord-#{version}' bundle exec rake test"
end
end

desc 'Measures test coverage'
task :coverage do
rm_f "coverage"
Expand Down
4 changes: 2 additions & 2 deletions flag_shih_tzu.gemspec
Expand Up @@ -8,9 +8,9 @@ Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.authors = ["Patryk Peszko", "Sebastian Roebke", "David Anderson", "Tim Payton"]
s.homepage = "https://github.com/xing/flag_shih_tzu"
s.summary = %q{A rails plugin to store a collection of boolean attributes in a single ActiveRecord column as a bit field}
s.summary = %q{An extension for ActiveRecord to store a collection of boolean attributes in a single integer column as a bit field.}
s.description = <<-EODOC
This plugin lets you use a single integer column in an ActiveRecord model
This gem lets you use a single integer column in an ActiveRecord model
to store a collection of boolean attributes (flags). Each flag can be used
almost in the same way you would use any boolean attribute on an
ActiveRecord object.
Expand Down
5 changes: 5 additions & 0 deletions gemfiles/Gemfile.activerecord-2.3.x
@@ -0,0 +1,5 @@
source "http://rubygems.org"

gemspec :path => '..'

gem "activerecord", "~>2.3.0"
5 changes: 5 additions & 0 deletions gemfiles/Gemfile.activerecord-3.0.x
@@ -0,0 +1,5 @@
source "http://rubygems.org"

gemspec :path => '..'

gem "activerecord", "~>3.0.0"
5 changes: 5 additions & 0 deletions gemfiles/Gemfile.activerecord-3.1.x
@@ -0,0 +1,5 @@
source "http://rubygems.org"

gemspec :path => '..'

gem "activerecord", "~>3.1.0"

0 comments on commit 9a7842d

Please sign in to comment.