= Swift

* http://github.com/shanna/swift

== Description

A rational rudimentary object relational mapper.

== Dependencies

* ruby   >= 1.9.1
* dbic++ >= 0.1.6
* mysql  >= 5.0.17 or postgresql >= 8.4

dbic++ can be found here http://github.com/deepfryed/dbicpp

== Features

* Multiple databases.
* Prepared statements.
* Bind values.
* Transactions and named save points.
* EventMachine asynchronous interface.
* Migrations.

== Performance

Swift prefers performance when it doesn't compromise the Ruby-ish interface. It's unfair to compare Swift to DataMapper
and ActiveRecord which suffer under the weight of support for many more databases and legacy/alternative Ruby
implementations. That said obviously if Swift were slower it would be redundant so benchmark code does exist in
http://github.com/shanna/swift/tree/master/benchmarks

== Synopsis

=== DB

  require 'swift'

  Swift.trace true # Debugging.
  Swift.setup :default, Swift::DB::Postgres, db: 'swift'

  # Block form db context.
  Swift.db do |db|
    db.execute('drop table if exists users')
    db.execute('create table users(id serial, name text, email text)')

    # Save points are supported.
    db.transaction :named_save_point do
      st = db.prepare('insert into users (name, email) values (?, ?)')
      puts st.execute('Apple Arthurton', 'apple@arthurton.local').insert_id
      puts st.execute('Benny Arthurton', 'benny@arthurton.local').insert_id
    end

    # Block result iteration.
    db.prepare('select * from users').execute do |row|
      puts row.inspect
    end

    # Enumerable.
    result = db.prepare('select * from users where name like ?').execute('Benny%')
    puts result.first
  end

=== DB Scheme Operations

Rudimentary object mapping. Provides a definition to the db methods for prepared (and cached) statements plus native
primitive Ruby type conversion.

  require 'swift'

  Swift.trace true # Debugging.
  Swift.setup :default, Swift::DB::Postgres, db: 'swift'

  class User < Swift::Scheme
    store     :users
    attribute :id,         Swift::Type::Integer, serial: true, key: true
    attribute :name,       Swift::Type::String
    attribute :email,      Swift::Type::String
    attribute :updated_at, Swift::Type::Time
  end # User

  Swift.db do |db|
    db.migrate! User

    # Select Scheme instance (relation) instead of Hash.
    users = db.prepare(User, 'select * from users limit 1').execute

    # Make a change and update.
    users.each{|user| user.updated_at = Time.now}
    db.update(User, *users)

    # Get a specific user by id.
    user = db.get(User, id: 1)
    puts user.name, user.email
  end

=== Scheme CRUD

Scheme/relation level helpers.

  require 'swift'

  Swift.trace true # Debugging.
  Swift.setup :default, Swift::DB::Postgres, db: 'swift'

  class User < Swift::Scheme
    store     :users
    attribute :id,    Swift::Type::Integer, serial: true, key: true
    attribute :name,  Swift::Type::String
    attribute :email, Swift::Type::String
  end # User

  # Migrate it.
  User.migrate!

  # Create
  User.create name: 'Apple Arthurton', email: 'apple@arthurton.local' # => User

  # Get by key.
  user = User.get id: 1

  # Alter attribute and update in one.
  user.update name: 'Jimmy Arthurton'

  # Alter attributes and update.
  user.name = 'Apple Arthurton'
  user.update

  # Destroy
  user.destroy

=== Conditions SQL syntax.

SQL is easy and most people know it so Swift ORM provides a simple symbol like syntax to convert resource
names to field names.

  class User < Swift::Scheme
    store     :users
    attribute :id,    Swift::Type::Integer, serial: true, key: true
    attribute :age,   Swift::Type::Integer, field: 'ega'
    attribute :name,  Swift::Type::String,  field: 'eman'
    attribute :email, Swift::Type::String,  field: 'liame'
  end # User

  # Convert :name and :age to fields.
  # select * from users where eman like '%Arthurton' and ega > 20
  users = User.all(':name like ? and :age > ?', '%Arthurton', 20)

=== Identity Map

Swift comes with a simple identity map. Just require it after you load swift.

  require 'swift'
  require 'swift/identity_map'

  class User < Swift::Scheme
    store     :users
    attribute :id,    Swift::Type::Integer, serial: true, key: true
    attribute :age,   Swift::Type::Integer, field: 'ega'
    attribute :name,  Swift::Type::String,  field: 'eman'
    attribute :email, Swift::Type::String,  field: 'liame'
  end # User

  User.first(':name = ?', 'James Arthurton')
  User.first(':name = ?', 'James Arthurton') # Gets same object reference


=== Bulk inserts

Swift comes with adapter level support for bulk inserts for MySQL and PostgreSQL. This
is usually very fast (~5-10x faster) than regular prepared insert statements for larger
sets of data.

MySQL adapter - Overrides the MySQL C API and implements its own _infile_ handlers. This
means currently you *cannot* execute the following SQL using Swift

  LOAD DATA LOCAL INFILE '/tmp/users.tab' INTO TABLE users;

But you can do it almost as fast in ruby,

  require 'swift'

  Swift.setup :default, Swift::DB::Mysql, db: 'swift'

  # MySQL packet size is the usual limit, 8k is the packet size by default.
  Swift.db do |db|
    File.open('/tmp/users.tab') do |file|
      count = db.write('users', %w{name email balance}, file)
    end
  end

You are not just limited to files - you can stream data from anywhere into MySQL and
PostgreSQL directly without creating temporary files.


== Benchmarks

The following bechmarks were run on a machine with 4G ram, 5200rpm sata drive,
Intel Core2Duo P8700 2.53GHz and stock PostgreSQL 8.4.1.

* 10,000 rows are created once.
* All the rows are selected once.
* All the rows are selected once and updated once.
* Memory footprint(rss) shows how much memory the benchmark used with GC disabled.
  This gives an idea of total memory use and indirectly an idea of the number of
  objects allocated and the pressure on Ruby GC if it were running. When GC is enabled,
  the actual memory consumption might be much lower than the numbers below.

    ./benchmarks/simple.rb -r 10000 -n 1

    benchmark       sys         user        total       real        rss
    dm #create      0.390000    3.950000    4.340000    5.771812    244.32m
    dm #select      0.150000    1.760000    1.910000    2.035583    128.97m
    dm #update      0.690000    7.880000    8.570000    11.295239   603.30m

    ar #create      0.930000    6.620000    7.550000    10.002911   367.82m
    ar #select      0.050000    0.310000    0.360000    0.417127     38.82m
    ar #update      0.770000    6.180000    6.950000    9.711788    361.93m

    swift #create   0.180000    0.820000    1.000000    1.968757     27.35m
    swift #select   0.010000    0.070000    0.080000    0.130234      9.85m
    swift #update   0.250000    0.610000    0.860000    1.996165     29.35m
    swift #write    0.000000    0.100000    0.100000    0.167199      6.23m


== TODO

* Tests.
* Assertions for dumb stuff. model < Model for methods in Adapter.
* Profile.

== Contributing

Go nuts! There is no style guide and I do not care if you write tests or comment code. If you write something neat just
send a pull request, tweet, email or yell it at me line by line in person.