sentiql 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ *.swp
3
+ *~
4
+ .bundle
5
+ Gemfile.lock
6
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sentiql.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'bundler'
2
+ require 'yaml'
3
+ require 'logger'
4
+ require 'active_record'
5
+
6
+ Bundler::GemHelper.install_tasks
7
+
8
+
9
+ MIGRATIONS_DIR = 'db/migrate'
10
+ APP_PATH = File.join(File.dirname(__FILE__))
11
+
12
+ namespace :db do
13
+
14
+ task :environment do
15
+ ENV['RACK_ENV'] ||= 'development'
16
+ @config = YAML.load_file('config/database.yml')[ENV['RACK_ENV']]
17
+ ActiveRecord::Base.establish_connection @config
18
+ end
19
+
20
+ desc 'Migrate the database (options: VERSION=x, VERBOSE=false).'
21
+ task :migrate => :environment do
22
+ ActiveRecord::Migration.verbose = true
23
+ ActiveRecord::Migrator.migrate MIGRATIONS_DIR, ENV['VERSION'] ? ENV['VERSION'].to_i : nil
24
+ end
25
+
26
+ desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
27
+ task :rollback => :configure_connection do
28
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
29
+ ActiveRecord::Migrator.rollback MIGRATIONS_DIR, step
30
+ end
31
+
32
+ desc "Retrieves the current schema version number"
33
+ task :version => :configure_connection do
34
+ puts "Current version: #{ActiveRecord::Migrator.current_version}"
35
+ end
36
+
37
+ namespace :test do
38
+
39
+ desc "Creates testing database by cloning main database"
40
+ task :load do
41
+ @config = YAML.load_file('config/database.yml')['test']
42
+ ActiveRecord::Base.establish_connection @config
43
+ tables = ActiveRecord::Base.connection.select_all("SHOW TABLES").map { |m| m.values.first }
44
+ tables.each do |table_name|
45
+ ActiveRecord::Base::connection.execute("DROP TABLE #{table_name}")
46
+ end
47
+ ActiveRecord::Migration.verbose = true
48
+ ActiveRecord::Migrator.migrate MIGRATIONS_DIR
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,7 @@
1
+ test:
2
+ adapter: mysql2
3
+ encoding: utf8
4
+ database: sentiql_test
5
+ username: root
6
+ password: salt
7
+ host: localhost
@@ -0,0 +1,24 @@
1
+ class TestTables < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :users do |t|
5
+ t.string :name
6
+ t.string :full_name
7
+ t.string :email
8
+ t.string :crypted_password
9
+ t.string :salt
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ create_table :after_save do |t|
15
+ t.timestamps
16
+ end
17
+ end
18
+
19
+ def self.down
20
+ drop_table :users
21
+ drop_table :after_save
22
+ end
23
+
24
+ end
@@ -0,0 +1,3 @@
1
+ module SentiQL
2
+ VERSION = "0.1.2"
3
+ end
data/lib/sentiql.rb ADDED
@@ -0,0 +1,136 @@
1
+ require "mysql2"
2
+
3
+ module SentiQL
4
+
5
+ class Base
6
+ attr_accessor :attrs, :errors
7
+
8
+ def initialize attrs={}
9
+ @attrs = {}
10
+ attrs.each_pair do |key, value|
11
+ @attrs[key.to_sym] = value
12
+ end
13
+ end
14
+
15
+ def [] key
16
+ @attrs ||= {}
17
+ @attrs[key]
18
+ end
19
+
20
+ def []=(key, value)
21
+ @attrs ||= {}
22
+ @attrs[key] = value
23
+ end
24
+
25
+
26
+ def save
27
+
28
+ filter_with :before_save_filters
29
+
30
+ if @attrs[:id]
31
+ i = @attrs.select { |k| self.class.schema.include?(k) }
32
+ values = i.keys.map { |k| @attrs[k] }
33
+ values << @attrs[:id]
34
+
35
+ return self unless valid?
36
+
37
+ SentiQL::Base.execute "UPDATE #{self.class.table} SET #{i.keys.map{|m| "#{m.to_s}=?"}.join(",")} WHERE id=?", values
38
+
39
+ else
40
+
41
+ filter_with :before_create_filters
42
+
43
+ i = @attrs.select { |k| self.class.schema.include?(k) }
44
+ values = i.keys.map { |k| @attrs[k] }
45
+
46
+ return self unless valid?
47
+
48
+ id = SentiQL::Base.insert "INSERT INTO #{self.class.table} (#{i.keys.map{|k| k.to_s}.join(",")}) VALUES (#{i.map{|k| k="?"}.join(",")})", values
49
+ @attrs[:id] = id
50
+
51
+ filter_with :after_create_filters
52
+ end
53
+
54
+ filter_with :after_save_filters
55
+
56
+ return self
57
+ end
58
+
59
+ def filter_with filter
60
+ self.class.send(filter).each do |f|
61
+ self.send f
62
+ end
63
+ end
64
+
65
+ def method_missing method_id, *args
66
+ if @attrs.has_key? method_id
67
+ return @attrs[method_id.to_sym]
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ def valid?; true; end
74
+
75
+ class << self
76
+ def connection; @@connection; end
77
+ def connection=(value); @@connection = value; end
78
+
79
+ def set_schema *args; @schema = args; end
80
+ def schema; @schema; end
81
+
82
+ def before_save *args; @before_save_filters = args; end
83
+ def before_save_filters; @before_save_filters ||={} ; end
84
+ def before_create *args; @before_create_filters = args; end
85
+ def before_create_filters; @before_create_filters ||={}; end
86
+ def after_create *args; @after_create_filters = args; end
87
+ def after_create_filters; @after_create_filters ||={}; end
88
+ def after_save *args; @after_save_filters= args; end
89
+ def after_save_filters; @after_save_filters ||={}; end
90
+
91
+ def set_table name; @table= name.to_s; end
92
+ def table; @table.to_sym; end
93
+
94
+
95
+ def create hash
96
+ obj = self.new hash
97
+ return obj.save
98
+ end
99
+
100
+ def find_by hash
101
+ field_name = hash.keys.first.to_s
102
+ value = hash.values.first
103
+ r = first "SELECT * FROM #{self.table} WHERE #{field_name}=? LIMIT 1", [value]
104
+ return r ? self.new(r) : nil
105
+ end
106
+
107
+ def first sql, args=[]
108
+ results = self.execute sql, args
109
+ return results.nil? ? nil : results.first
110
+ end
111
+
112
+ def insert sql, args=[]
113
+ execute sql, args
114
+ connection.last_id
115
+ end
116
+
117
+ def execute sql, args=[]
118
+ esced = args.map { |v| connection.escape(v.to_s) }
119
+ ix = 0
120
+ escq = sql.gsub(/\?/) do |m|
121
+ raise Mysql2::Error.new("Not enough arguments for prepared statement") if ix >= esced.count
122
+ val = esced[ix]
123
+ ix += 1
124
+ "'#{val}'"
125
+ end
126
+ raise Mysql2::Error.new("Too many arguments for prepared statement (required: #{ix}, passed: #{esced.count})") if ix < esced.count
127
+ return connection.query escq
128
+ end
129
+
130
+ alias :all :execute
131
+
132
+ end
133
+
134
+ end
135
+
136
+ end
data/sentiql.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sentiql/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sentiql"
7
+ s.version = SentiQL::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Martynas Miliauskas"]
10
+ s.email = ["miliauskas@facebook.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{A minimalistic Ruby wrapper for MySQL}
13
+ s.description = %q{This is a work in progress. SentiQL promotes use of SQL for data selection, OM for CUD operations and being as lean as possible.}
14
+
15
+ s.rubyforge_project = "sentiql"
16
+
17
+ s.add_runtime_dependency 'mysql2', '< 0.3'
18
+
19
+ s.add_development_dependency 'rspec'
20
+ s.add_development_dependency 'database_cleaner'
21
+ s.add_development_dependency 'activerecord', '~> 3.0.5'
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,134 @@
1
+ require_relative 'spec_helper'
2
+ require 'date'
3
+
4
+ class User < SentiQL::Base
5
+
6
+ set_schema :name, :full_name, :email, :crypted_password, :salt, :created_at, :updated_at
7
+ set_table :users
8
+
9
+ before_save :set_updated_at
10
+ before_create :set_created_at
11
+ after_create :touch_after_create
12
+ after_save :touch_after_save
13
+
14
+ class << self
15
+
16
+ def count
17
+ r = self.first "SELECT count(*) AS count FROM #{table}"
18
+ r[:count]
19
+ end
20
+
21
+ end
22
+
23
+ protected
24
+
25
+ def set_updated_at
26
+ self[:updated_at] = Time.now
27
+ end
28
+
29
+ def set_created_at
30
+ self[:created_at] = Time.now
31
+ end
32
+
33
+ def touch_after_create
34
+ self[:after_create_touched] = Time.now
35
+ end
36
+
37
+ def touch_after_save
38
+ self[:after_save_touched] = Time.now
39
+ end
40
+
41
+ end
42
+
43
+
44
+ describe SentiQL::Base do
45
+
46
+ describe User do
47
+
48
+ describe '.create' do
49
+ it 'creates a new record in DB' do
50
+ lambda { User.create :name=>'Natalie' }.should change(User, :count).by(1)
51
+ end
52
+
53
+ it 'returns and instance of User' do
54
+ u = User.create :name=>'Natalie'
55
+ u.should be_an_instance_of(User)
56
+ end
57
+
58
+ it 'sets :id of record in DB to an instance' do
59
+ u = User.create :name=>'Natalie'
60
+ u[:id].should_not be_nil
61
+ end
62
+ end
63
+
64
+ describe '.save' do
65
+ it "creates new record if instance is new" do
66
+ u = User.new :name=>'Natalie'
67
+ lambda{ u.save }.should change(User, :count).by(1)
68
+ end
69
+
70
+ it "updates existing record if instance was already created" do
71
+ u = User.new :name=>'Natalie'
72
+ u.save
73
+ u[:full_name] = 'Natalie Portman'
74
+ lambda { u.save }.should_not change(User, :count)
75
+ end
76
+ end
77
+
78
+ describe 'filters' do
79
+
80
+ before do
81
+ end
82
+
83
+ it "executes before_save every time instance is saved" do
84
+ u = User.new :name=>'Natalie'
85
+ u.save
86
+ sleep(1)
87
+ lambda { u.save }.should change(u, :updated_at)
88
+ end
89
+
90
+ it "executes before_create filters only when record is created" do
91
+ u = User.new :name=>'Natalie'
92
+ u.save
93
+ sleep(1)
94
+ lambda { u.save }.should_not change(u, :created_at)
95
+ end
96
+
97
+ it "executes after_create filters only after record is created" do
98
+ u = User.new :name=>'Natalie'
99
+ u.save
100
+ lambda { u.save }.should_not change(u, :after_create_touched)
101
+ end
102
+
103
+ it "executes after_save filters after everytime instance is saved" do
104
+ u = User.new :name=>'Natalie'
105
+ u.save
106
+ lambda { u.save }.should change(u, :after_save_touched)
107
+ end
108
+
109
+ end
110
+ end
111
+
112
+ describe '.first' do
113
+ it 'returns nil when no records found' do
114
+ r = SentiQL.first 'SHOW TABLES LIKE ?', ['non_existing_tbl%']
115
+ r.should == nil
116
+ end
117
+
118
+ it 'returns a hash if record found with keys representing column values' do
119
+ r = SentiQL.first 'SELECT CONCAT(?,?) AS str', ['a','b']
120
+ r.should be_an_instance_of(Hash)
121
+ r[:str].should == 'ab'
122
+ end
123
+ end
124
+
125
+ describe '.all' do
126
+ it 'returns Mysql2::Result which holds data in Hashes' do
127
+ r = SentiQL.all 'SHOW OPEN TABLES'
128
+ r.should be_an_instance_of(Mysql2::Result)
129
+ r.first.should be_an_instance_of(Hash)
130
+ end
131
+ end
132
+
133
+
134
+ end
@@ -0,0 +1,40 @@
1
+ require 'database_cleaner'
2
+ require 'yaml'
3
+ require 'rspec'
4
+ require 'active_record'
5
+ require 'mysql2'
6
+
7
+ require 'sentiql'
8
+
9
+ ROOT_PATH = File.dirname(__FILE__)
10
+
11
+
12
+ @config = YAML.load_file(File.join(ROOT_PATH, '..', "config/database.yml"))['test']
13
+
14
+ DB = Mysql2::Client.new(
15
+ :host=>@config["host"],
16
+ :username=>@config["username"],
17
+ :password=>@config["password"],
18
+ :database=>@config["database"]
19
+ )
20
+ DB.query_options.merge!(:symbolize_keys=>true)
21
+ SentiQL::Base.connection = DB
22
+
23
+ ActiveRecord::Base.establish_connection @config
24
+
25
+ RSpec.configure do |config|
26
+
27
+ config.before(:suite) do
28
+ DatabaseCleaner.strategy = :transaction
29
+ DatabaseCleaner.clean_with(:truncation)
30
+ end
31
+
32
+ config.before(:each) do
33
+ DatabaseCleaner.start
34
+ end
35
+
36
+ config.after(:each) do
37
+ DatabaseCleaner.clean
38
+ end
39
+
40
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sentiql
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.2
6
+ platform: ruby
7
+ authors:
8
+ - Martynas Miliauskas
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-15 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: mysql2
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - <
23
+ - !ruby/object:Gem::Version
24
+ version: "0.3"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: database_cleaner
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: activerecord
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: 3.0.5
58
+ type: :development
59
+ version_requirements: *id004
60
+ description: This is a work in progress. SentiQL promotes use of SQL for data selection, OM for CUD operations and being as lean as possible.
61
+ email:
62
+ - miliauskas@facebook.com
63
+ executables: []
64
+
65
+ extensions: []
66
+
67
+ extra_rdoc_files: []
68
+
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - Rakefile
73
+ - config/database.yml
74
+ - db/migrate/20110515_test_tables.rb
75
+ - lib/sentiql.rb
76
+ - lib/sentiql/version.rb
77
+ - sentiql.gemspec
78
+ - spec/sentiql_spec.rb
79
+ - spec/spec_helper.rb
80
+ has_rdoc: true
81
+ homepage: ""
82
+ licenses: []
83
+
84
+ post_install_message:
85
+ rdoc_options: []
86
+
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project: sentiql
104
+ rubygems_version: 1.6.2
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: A minimalistic Ruby wrapper for MySQL
108
+ test_files: []
109
+