Permalink
Browse files

Initial import.

  • Loading branch information...
1 parent 9922aaf commit dde293f2e7f44fbadce417b3333a6682cbdc7baa Michael Bleigh committed Mar 9, 2010
Showing with 352 additions and 24 deletions.
  1. +1 −1 LICENSE
  2. +4 −3 README.rdoc
  3. +12 −9 Rakefile
  4. BIN gloo
  5. +60 −0 gloo.gemspec
  6. +139 −0 lib/gloo/active_record.rb
  7. +94 −0 spec/gloo/active_record_spec.rb
  8. +0 −7 spec/gloo_spec.rb
  9. BIN spec/gloo_test.sqlite3.db
  10. +11 −0 spec/schema.rb
  11. +31 −4 spec/spec_helper.rb
View
2 LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2009 Michael Bleigh
+Copyright (c) 2010 Intridea, Inc. and Michael Bleigh
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
View
7 README.rdoc
@@ -1,6 +1,7 @@
-= gloo
+= Gloo
-Description goes here.
+Gloo is built to allow for cross-ORM relationships. It is not ready for public
+release yet, so please move along. Nothing to see here.
== Note on Patches/Pull Requests
@@ -15,4 +16,4 @@ Description goes here.
== Copyright
-Copyright (c) 2010 Michael Bleigh. See LICENSE for details.
+Copyright (c) 2010 Intridea, Inc. and Michael Bleigh. See LICENSE for details.
View
21 Rakefile
@@ -5,27 +5,30 @@ begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "gloo"
- gem.summary = %Q{TODO: one-line summary of your gem}
- gem.description = %Q{TODO: longer description of your gem}
+ gem.summary = %Q{Different ORMs living in the same app? Gloo them together!}
+ gem.description = %Q{Different ORMs living in the same app? Gloo them together!}
gem.email = "michael@intridea.com"
- gem.homepage = "http://github.com/mbleigh/gloo"
+ gem.homepage = "http://github.com/intridea/gloo"
gem.authors = ["Michael Bleigh"]
gem.add_development_dependency "rspec", ">= 1.2.9"
+ gem.add_dependency 'activerecord', '>= 3.0.0.beta'
+ gem.add_dependency 'activesupport', '>= 3.0.0.beta'
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
end
-require 'spec/rake/spectask'
-Spec::Rake::SpecTask.new(:spec) do |spec|
- spec.libs << 'lib' << 'spec'
- spec.spec_files = FileList['spec/**/*_spec.rb']
+require 'rspec/core/rake_task'
+Rspec::Core::RakeTask.new(:spec) do |spec|
+ #spec.libs << 'lib' << 'spec'
+ #spec.spec_files = FileList['spec/**/*_spec.rb']
+ spec.pattern = 'spec/**/*_spec.rb'
end
-Spec::Rake::SpecTask.new(:rcov) do |spec|
- spec.libs << 'lib' << 'spec'
+Rspec::Core::RakeTask.new(:rcov) do |spec|
+ #spec.libs << 'lib' << 'spec'
spec.pattern = 'spec/**/*_spec.rb'
spec.rcov = true
end
View
BIN gloo
Binary file not shown.
View
60 gloo.gemspec
@@ -0,0 +1,60 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{gloo}
+ s.version = "0.0.0.alpha.1"
+
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Michael Bleigh"]
+ s.date = %q{2010-03-08}
+ s.description = %q{Different ORMs living in the same app? Gloo them together!}
+ s.email = %q{michael@intridea.com}
+ s.extra_rdoc_files = [
+ "LICENSE",
+ "README.rdoc"
+ ]
+ s.files = [
+ ".document",
+ ".gitignore",
+ "LICENSE",
+ "README.rdoc",
+ "Rakefile",
+ "VERSION",
+ "lib/gloo.rb",
+ "spec/spec.opts",
+ "spec/spec_helper.rb"
+ ]
+ s.homepage = %q{http://github.com/intridea/gloo}
+ s.rdoc_options = ["--charset=UTF-8"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.6}
+ s.summary = %q{Different ORMs living in the same app? Gloo them together!}
+ s.test_files = [
+ "spec/gloo/active_record_spec.rb",
+ "spec/schema.rb",
+ "spec/spec_helper.rb"
+ ]
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0.beta"])
+ s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0.beta"])
+ else
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
+ s.add_dependency(%q<activerecord>, [">= 3.0.0.beta"])
+ s.add_dependency(%q<activesupport>, [">= 3.0.0.beta"])
+ end
+ else
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
+ s.add_dependency(%q<activerecord>, [">= 3.0.0.beta"])
+ s.add_dependency(%q<activesupport>, [">= 3.0.0.beta"])
+ end
+end
+
View
139 lib/gloo/active_record.rb
@@ -0,0 +1,139 @@
+gem 'activerecord'
+require 'active_record'
+
+module Gloo
+ module ActiveRecord
+ def self.included(base)
+ base.extend ClassMethods
+ end
+
+ class Proxy
+ def initialize(klass)
+ @klass = klass
+ end
+
+ # Create a one-to-one relationship between this class and
+ # an ActiveRecord model. This should only be done if this
+ # class contains a foreign key attribute referencing the.
+ # model in question. Options supported:
+ #
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that
+ # name can‘t be inferred from the association name. So has_one
+ # <tt>:author</tt> will by default be linked to the Author class,
+ # but if the real class name is Person, you‘ll have to specify it
+ # with this option.
+ # [:conditions]
+ # Specify the conditions that the associated object must meet in
+ # order to be included as a <tt>WHERE</tt> SQL fragment, such as
+ # <tt>authorized = 1</tt>.
+ # [:include]
+ # Specify second-order associations that should be eager loaded
+ # when this object is loaded.
+ # [:select]
+ # Select specific fields to return in the <tt>SELECT</tt> statement.
+ # [:foreign_key]
+ # The method on this class that will yield a valid primary key for
+ # the associated ActiveRecord model.
+ # [:primary_key]
+ # The column in the database to which this association is keyed.
+ def belongs_to(association_id, options = {})
+ @klass._activerecord_belongs_to association_id, options
+ end
+
+ def method_missing(name, *args)
+ @klass.send("_activerecord_#{name}", *args)
+ end
+ end
+
+ module ClassMethods
+ @@_activerecord_proxies = {}
+
+ def gloo(to, &block)
+ case to
+ when :active_record
+ @proxy = Gloo::ActiveRecord::Proxy.new(self).instance_eval(&block)
+ else
+ super if respond_to?(:super)
+ end
+ end
+
+ def _activerecord_proxies
+ @@_activerecord_proxies
+ end
+
+ def _activerecord_belongs_to(association_id, options = {})
+ association_id = association_id.to_s
+
+ opts = {
+ :class_name => association_id.classify,
+ :foreign_key => "#{association_id}_id",
+ :primary_key => "id"
+ }.merge(options)
+
+ proxy = opts[:class_name].classify.constantize
+
+ proxy = proxy.where(opts[:conditions]) if opts[:conditions]
+ proxy = proxy.includes(*opts[:include]) if opts[:include]
+ proxy = proxy.select(opts[:select]) if opts[:select]
+
+ (_activerecord_proxies[:belongs_to] ||= {})[association_id] = proxy
+
+ class_eval <<-RUBY
+ def #{association_id}
+ return nil unless self.send(#{opts[:foreign_key].inspect})
+ self.class._activerecord_proxies[:belongs_to][#{association_id.inspect}].where(#{opts[:primary_key].inspect} => self.#{opts[:foreign_key]}).first
+ end
+
+ def #{association_id}=(associate)
+ self.#{opts[:foreign_key]} = associate.#{opts[:primary_key]}
+ end
+
+ def create_#{association_id}(attributes = {})
+ self.#{association_id} = #{opts[:class_name]}.create(attributes)
+ end
+ RUBY
+ end
+
+ def _activerecord_has_many(association_id, options = {}, &extension)
+ association_id = association_id.to_s.underscore
+
+ opts = {
+ :class_name => association_id.singularize.classify,
+ :foreign_key => "#{self.name.underscore.singularize}_id",
+ :primary_key => "id"
+ }.merge(options)
+
+ proxy = opts[:class_name].classify.constantize
+
+ proxy = proxy.where(opts[:conditions]) if opts[:conditions]
+ proxy = proxy.includes(*opts[:include]) if opts[:include]
+ proxy = proxy.select(opts[:select]) if opts[:select]
+
+ (_activerecord_proxies[:has_many] ||= {})[association_id] = proxy
+
+ class_eval <<-RUBY
+ def #{association_id}
+ self.class._activerecord_proxies[:has_many][#{association_id.inspect}].where(#{opts[:foreign_key].inspect} => self.#{opts[:primary_key]}).all
+ end
+
+ def #{association_id}=(associates)
+ associates.each{|a| a.update_attributes(#{opts[:foreign_key].inspect} => self.#{opts[:primary_key]})}
+ end
+
+ def #{association_id.singularize}(attributes = {})
+ self.#{association_id} = #{opts[:class_name]}.create(attributes).where()
+ end
+
+ def #{association_id.singularize}_ids
+ self.#{association_id}.select(:id).all.collect{|a| a.id}
+ end
+
+ def #{association_id.singularize}_ids=(new_ids)
+ self.class._activerecord_proxies[:has_many][#{association_id.inspect}].where(:id => new_ids).update_all(#{opts[:foreign_key].inspect} => self.#{opts[:primary_key]})
+ end
+ RUBY
+ end
+ end
+ end
+end
View
94 spec/gloo/active_record_spec.rb
@@ -0,0 +1,94 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+require 'gloo/active_record'
+
+describe Gloo::ActiveRecord do
+ before(:each) do
+ Object.send(:remove_const, :GlooModel) if defined? GlooModel
+ class GlooModel
+ include Gloo::ActiveRecord
+ attr_accessor :user_id, :id
+ end
+ end
+
+ it 'should provide a .gloo method to the class' do
+ GlooModel.should be_respond_to(:gloo)
+ end
+
+ describe ' belongs_to' do
+ it 'should be available' do
+ GlooModel.should be_respond_to(:_activerecord_belongs_to)
+ end
+
+ it 'should define appropriate methods after being called' do
+ GlooModel.gloo :active_record do
+ belongs_to :user
+ end
+
+ %w(user user= create_user).each do |meth|
+ GlooModel.instance_methods.should be_include(meth)
+ end
+ end
+
+ describe ' with default foreign and primary keys' do
+ before(:each) do
+ GlooModel.gloo :active_record do
+ belongs_to :user
+ end
+ end
+
+ it '#<association> should return an ActiveRecord with the specified ID' do
+ g = GlooModel.new
+ u = User.create
+ g.user_id = u.id
+ g.user.should == u
+ end
+
+ it '#<association>= should set the foreign key appropriately' do
+ g = GlooModel.new
+ u = User.create
+ g.user = u
+ g.user_id.should == u.id
+ end
+ end
+ end
+
+ describe ' has_many' do
+ it 'should be available' do
+ GlooModel.should be_respond_to(:_activerecord_has_many)
+ end
+
+ it 'should define appropriate methods after being called' do
+ GlooModel.gloo :active_record do
+ has_many :comments
+ end
+
+ %w(comments comments= comment_ids comment_ids=).each do |meth|
+ GlooModel.instance_methods.should be_include(meth)
+ end
+ end
+
+ describe ' with default options' do
+ before(:each) do
+ Comment.delete_all
+ GlooModel.gloo :active_record do
+ has_many :comments
+ end
+ end
+
+ it "#<association> should return all ActiveRecords with this model's id" do
+ g = GlooModel.new
+ g.id = 'bob'
+ 7.times{ Comment.create(:gloo_model_id => 'bob')}
+ g.comments.size.should == 7
+ end
+
+ it '#<association>= should associate the specified models with this model' do
+ g = GlooModel.new
+ g.id = 'frank'
+ comments = [Comment.create, Comment.create, Comment.create]
+ g.comments = comments
+ comments.each {|c| c.reload.gloo_model_id.should == 'frank'}
+ end
+ end
+ end
+end
View
7 spec/gloo_spec.rb
@@ -1,7 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
-
-describe "Gloo" do
- it "fails" do
- fail "hey buddy, you should probably rename this file and start specing for real"
- end
-end
View
BIN spec/gloo_test.sqlite3.db
Binary file not shown.
View
11 spec/schema.rb
@@ -0,0 +1,11 @@
+ActiveRecord::Schema.define(:version => 0) do
+ create_table :users do |t|
+ t.string :name
+ end
+
+ create_table :comments do |t|
+ t.string :gloo_model_id
+ t.text :body
+ end
+end
+
View
35 spec/spec_helper.rb
@@ -1,9 +1,36 @@
$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
-require 'gloo'
-require 'spec'
-require 'spec/autorun'
+require 'rubygems'
+require 'rspec'
+require 'rspec/autorun'
-Spec::Runner.configure do |config|
+require 'active_record'
+require 'logger'
+
+ActiveRecord::Base.establish_connection(
+ :adapter => 'sqlite3',
+ :dbfile => 'gloo_test.sqlite3.db',
+ :database => 'gloo'
+)
+
+TEST_DATABASE_FILE = File.join(File.dirname(__FILE__), 'gloo_test.sqlite3.db')
+
+File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
+ActiveRecord::Base.establish_connection(
+ "adapter" => "sqlite3", "database" => TEST_DATABASE_FILE
+)
+
+ActiveRecord::Base.logger = Logger.new(STDOUT)
+
+ActiveRecord::Base.silence do
+ ActiveRecord::Migration.verbose = false
+ load(File.dirname(__FILE__) + '/schema.rb')
+end
+
+class User < ActiveRecord::Base
end
+
+class Comment < ActiveRecord::Base
+ belongs_to :gloo_model
+end

0 comments on commit dde293f

Please sign in to comment.