Permalink
Browse files

Moving files from cramp and updating module names

  • Loading branch information...
1 parent 8c958e0 commit 41f41f0ff9da2df93dc20209677db04adace3342 @lifo committed Jul 17, 2010
View
@@ -0,0 +1 @@
+.bundle
View
10 Gemfile
@@ -0,0 +1,10 @@
+source 'http://rubygems.org'
+
+gem "tramp", :path => File.dirname(__FILE__)
+
+gem 'activesupport', '3.0.0.beta4'
+gem 'activemodel', '3.0.0.beta4'
+
+gem "arel", '>= 0.4.0'
+gem "mysqlplus", "0.1.1"
+gem "eventmachine", "0.12.10"
View
@@ -0,0 +1,20 @@
+Copyright (c) 2009-2010 Pratik Naik
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
@@ -0,0 +1,31 @@
+require "rubygems"
+require "bundler"
+Bundler.setup
+
+require 'tramp'
+
+Tramp.init(:username => 'root', :database => 'arel_development')
+
+class User < Tramp::Base
+ attribute :id, :type => Integer, :primary_key => true
+ attribute :name
+
+ validates_presence_of :name
+end
+
+EM.run do
+ user = User.new
+
+ user.save do |status|
+ if status.success?
+ puts "WTF!"
+ else
+ puts "Oops. Found errors : #{user.errors.inspect}"
+
+ user.name = 'Lush'
+ user.save do
+ User.where(User[:name].eq('Lush')).all {|users| puts users.inspect; EM.stop }
+ end
+ end
+ end
+end
View
@@ -0,0 +1,49 @@
+require 'eventmachine'
+EM.epoll
+
+require 'active_support'
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/aliasing'
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/core_ext/kernel/reporting'
+require 'active_support/concern'
+require 'active_support/core_ext/hash/indifferent_access'
+require 'active_support/buffered_logger'
+
+require 'tramp/evented_mysql'
+require 'tramp/emysql_ext'
+require 'mysqlplus'
+require 'arel'
+require 'tramp/arel_monkey_patches'
+require 'active_model'
+
+module Tramp
+ VERSION = '0.1'
+
+ mattr_accessor :logger
+
+ autoload :Quoting, "tramp/quoting"
+ autoload :Engine, "tramp/engine"
+ autoload :Column, "tramp/column"
+ autoload :Relation, "tramp/relation"
+
+ autoload :Base, "tramp/base"
+ autoload :Finders, "tramp/finders"
+ autoload :Attribute, "tramp/attribute"
+ autoload :AttributeMethods, "tramp/attribute_methods"
+ autoload :Status, "tramp/status"
+ autoload :Callbacks, "tramp/callbacks"
+
+ def self.init(settings)
+ Arel::Table.engine = Tramp::Engine.new(settings)
+ end
+
+ def self.select(query, callback = nil, &block)
+ callback ||= block
+
+ EventedMysql.select(query) do |rows|
+ callback.arity == 1 ? callback.call(rows) : callback.call if callback
+ end
+ end
+end
@@ -0,0 +1,66 @@
+class Arel::Session
+ def create(insert, &block)
+ insert.call(&block)
+ end
+
+ def read(select, &block)
+ select.call(&block)
+ end
+
+ def update(update, &block)
+ update.call(&block)
+ end
+
+ def delete(delete, &block)
+ delete.call(&block)
+ end
+
+end
+
+module Arel::Relation
+ def call(&block)
+ engine.read(self, &block)
+ end
+
+ def all(&block)
+ session.read(self) {|rows| block.call(rows) }
+ end
+
+ def first(&block)
+ session.read(self) {|rows| block.call(rows[0]) }
+ end
+
+ def each(&block)
+ session.read(self) {|rows| rows.each {|r| block.call(r) } }
+ end
+
+ def insert(record, &block)
+ session.create(Arel::Insert.new(self, record), &block)
+ end
+
+ def update(assignments, &block)
+ session.update(Arel::Update.new(self, assignments), &block)
+ end
+
+ def delete(&block)
+ session.delete(Arel::Deletion.new(self), &block)
+ end
+end
+
+class Arel::Deletion
+ def call(&block)
+ engine.delete(self, &block)
+ end
+end
+
+class Arel::Insert
+ def call(&block)
+ engine.create(self, &block)
+ end
+end
+
+class Arel::Update
+ def call(&block)
+ engine.update(self, &block)
+ end
+end
View
@@ -0,0 +1,101 @@
+# Copyright (c) 2009 Koziarski Software Ltd
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+module Tramp
+ class Attribute
+
+ FORMATS = {}
+ FORMATS[Date] = /^\d{4}\/\d{2}\/\d{2}$/
+ FORMATS[Integer] = /^-?\d+$/
+ FORMATS[Float] = /^-?\d*\.\d*$/
+ FORMATS[Time] = /\A\s*
+ -?\d+-\d\d-\d\d
+ T
+ \d\d:\d\d:\d\d
+ (\.\d*)?
+ (Z|[+-]\d\d:\d\d)?
+ \s*\z/ix # lifted from the implementation of Time.xmlschema
+
+ CONVERTERS = {}
+ CONVERTERS[Date] = Proc.new do |str|
+ Date.strptime(str, "%Y/%m/%d")
+ end
+
+ CONVERTERS[Integer] = Proc.new do |str|
+ Integer(str)
+ end
+
+ CONVERTERS[Float] = Proc.new do |str|
+ Float(str)
+ end
+
+ CONVERTERS[Time] = Proc.new do |str|
+ Time.xmlschema(str)
+ end
+
+ attr_reader :name
+ def initialize(name, owner_class, options)
+ @name = name.to_s
+ @owner_class = owner_class
+ @options = options
+
+ # append_validations!
+ define_methods!
+ end
+
+ # I think this should live somewhere in Amo
+ def check_value!(value)
+ # Allow nil and Strings to fall back on the validations for typecasting
+ # Everything else gets checked with is_a?
+ if value.nil?
+ nil
+ elsif value.is_a?(String)
+ value
+ elsif value.is_a?(expected_type)
+ value
+ else
+ raise TypeError, "Expected #{expected_type.inspect} but got #{value.inspect}"
+ end
+ end
+
+ def expected_type
+ @options[:type] || String
+ end
+
+ def type_cast(value)
+ if value.is_a?(expected_type)
+ value
+ elsif (converter = CONVERTERS[expected_type]) && (value =~ FORMATS[expected_type])
+ converter.call(value)
+ else
+ value
+ end
+ end
+
+ def append_validations!
+ if f = FORMATS[expected_type]
+ @owner_class.validates_format_of @name, :with => f, :unless => lambda {|obj| obj.send(name).is_a? expected_type }, :allow_nil => @options[:allow_nil]
+ end
+ end
+
+ def define_methods!
+ @owner_class.define_attribute_methods(true)
+ end
+
+ def primary_key?
+ @options[:primary_key]
+ end
+
+ end
+end
@@ -0,0 +1,81 @@
+# Copyright (c) 2009 Koziarski Software Ltd
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+module Tramp
+ module AttributeMethods
+
+ extend ActiveSupport::Concern
+ include ActiveModel::AttributeMethods
+
+ module ClassMethods
+ def attribute(name, options = {})
+ write_inheritable_hash(:model_attributes, {name => Attribute.new(name, self, options)})
+ end
+
+ def define_attribute_methods(force = false)
+ return unless model_attributes
+ undefine_attribute_methods if force
+ super(model_attributes.keys)
+ end
+ end
+
+ included do
+ class_inheritable_hash :model_attributes
+ undef_method(:id) if method_defined?(:id)
+
+ attribute_method_suffix("", "=")
+ end
+
+ def write_attribute(name, value)
+ if ma = self.class.model_attributes[name.to_sym]
+ value = ma.check_value!(value)
+ end
+ if(@attributes[name] != value)
+ send "#{name}_will_change!".to_sym
+ @attributes[name] = value
+ end
+ end
+
+ def read_attribute(name)
+ if ma = self.class.model_attributes[name]
+ ma.type_cast(@attributes[name])
+ else
+ @attributes[name]
+ end
+ end
+
+ def attributes=(attributes)
+ attributes.each do |(name, value)|
+ send("#{name}=", value)
+ end
+ end
+
+ protected
+
+ def attribute_method?(name)
+ @attributes.include?(name.to_sym) || model_attributes[name.to_sym]
+ end
+
+ private
+
+ def attribute(name)
+ read_attribute(name.to_sym)
+ end
+
+ def attribute=(name, value)
+ write_attribute(name.to_sym, value)
+ end
+
+ end
+end
Oops, something went wrong.

9 comments on commit 41f41f0

I'd really recommend you look at mysql2 EM module - much better and more modern API. EventedMySQL is great, but it's a bit of a hack.

Owner

lifo replied Jul 18, 2010

Yeah I was contemplating doing that. But now with 1.9 + Fibers + em_synchrony, I'm not too sure if we even need a separate async ORM. And that was one of the big reasons I moved it out of Cramp.

Well, the fiber stuff would be layered on top (em-synchrony), but you still need to pick an underlying async driver. I've made a fork (em-mysqlplus) of aman's original code and cleaned it up a bit, but mysql2 is probably the better bet at this point (they have an em driver inside). Now just need to build a small wrapper for the mysql2 em driver to work with synchrony, and you should be good to go.

Owner

lifo replied Jul 18, 2010

Yeah I understand the technical details. I'm just not sure if we need a special async ORM now that you can just use AR asynchronously.

Owner

lifo replied Jul 18, 2010

Or am I missing something ?

tobi replied Jul 18, 2010

the mysql2 gem will give you a lot of stuff for free. The main thing is that you don't have to do type casting anymore because that's done in C for you and that ends up being a tremendous performance win for larger result sets. That's just one of many reasons on why you want to use it.

Also FYI - mperham recently contributed a fibered AR adapter that shiped with mysql2 0.1.9

Owner

lifo replied Jul 18, 2010

I think we're all on the same page here :) I love mysql2 gem. And as Brian pointed out, it already has AR adapter. Because of the same, I see tramp as a dead library - you should just use AR.

Ah, yes.. now I'm with you - behind on caffeine today. Yes, you're right, you can just use straight up AR, just swap in mysql2 as the driver name in the config.

On a different, but related note.. Tobi, to your comments about type casting: I've done some tests on the gem, and while the base API is faster across the board, I didn't find the casting to be of any noticeable difference, with only exception: Date class. Parsing dates in C is vs using Date.parse, or even Time.parse makes a huge difference. So, when you run the mixed benchmarks, the difference you see is all basically due to that difference.

Please sign in to comment.