active_record_handlersocket 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ vendor
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in active_record_handlersocket.gemspec
4
+ gemspec
5
+ gem 'ruby-debug', '0.10.0'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Takayuki Sugita
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,86 @@
1
+ active_record_handlersocket
2
+ ===========================
3
+
4
+ HandlerSocket for ActiveRecord; depends handlersocket gem https://github.com/miyucy/handlersocket
5
+
6
+
7
+ **Underconstruction**
8
+
9
+ usage
10
+ ------------------------------------------------------------
11
+
12
+ Update your `config/database.yml` of rails project. (Available to set database same as AR read/write database.)
13
+
14
+ ```
15
+ development_hs_read:
16
+ host: localhost
17
+ port: 9998
18
+ database: active_record_handler_socket
19
+ ```
20
+
21
+ Define HandlerSocket index setting on your ActiveReocrd Model.
22
+
23
+ ```ruby
24
+ class Person < ActiveRecord::Base
25
+ handlersocket :id, "PRIMARY", %W[id name age]
26
+ end
27
+ ```
28
+
29
+ Call `hsfind_by_#{key}` of `hsfind_multi_by_#{key}` to get record(s) as ActiveRecord Object.
30
+
31
+ ```ruby
32
+ Person.hsfind_by_id(1)
33
+ #=> #<Person id: 1, name: "Bob Marley", age: 36>
34
+
35
+ Person.hsfind_multi_by_id(1, 2)
36
+ #=> [
37
+ # #<Person id: 1, name: "Bob Marley", age: 36>,
38
+ # #<Person id: 2, name: "Pharrell Wiiliams", age: 41>
39
+ # ]
40
+ ```
41
+
42
+
43
+ development
44
+ ------------------------------------------------------------
45
+
46
+ Dev dependencies
47
+
48
+ ```sh
49
+ mkdir vendor
50
+ bundle install --path=vendor
51
+ ```
52
+
53
+ Prepare DB
54
+
55
+ ```sh
56
+ rake db:prepare
57
+ ```
58
+
59
+ create following items on MySQL.
60
+
61
+ key | value
62
+ -----------------|------------------------------------------
63
+ user | rails
64
+ database (dev) | active_record_handler_socket
65
+ database (test) | active_record_handler_socket_test
66
+ tables | people, hobbies
67
+
68
+
69
+ Try example on console
70
+
71
+ ```sh
72
+ bundle exec irb
73
+ ```
74
+
75
+ ```ruby
76
+ require 'examples/init'
77
+ #=> true
78
+
79
+ Person.create(:name => "Bob Marley", :age => 36, :status => false)
80
+
81
+ Person.find_by_id(1)
82
+ #=> #<Person id: 1, name: "Bob Marley", age: 36, status: false>
83
+ Person.hsfind_by_id(1)
84
+ #=> #<Person id: 1, name: "Bob Marley", age: 36, status: false>
85
+ ```
86
+
@@ -0,0 +1,87 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => [:spec]
4
+
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = "spec/**/*_spec.rb"
8
+ spec.rspec_opts = ["-cfs"]
9
+ end
10
+
11
+ namespace :db do
12
+ USER = "rails"
13
+
14
+ DATABASES = %W[
15
+ active_record_handler_socket
16
+ active_record_handler_socket_test
17
+ ]
18
+
19
+ TABLES = {
20
+ :people => %W[
21
+ id int(11) NOT NULL AUTO_INCREMENT,
22
+ name varchar(255) DEFAULT '',
23
+ age int(11) DEFAULT NULL,
24
+ status tinyint(1) NOT NULL DEFAULT '1',
25
+ PRIMARY KEY (id)
26
+ ].join(" "),
27
+ :hobbies => %W[
28
+ id int(11) NOT NULL AUTO_INCREMENT,
29
+ person_id int(11) NOT NULL,
30
+ title varchar(255) DEFAULT '',
31
+ created_at datetime DEFAULT NULL,
32
+ updated_at datetime DEFAULT NULL,
33
+ PRIMARY KEY (id),
34
+ KEY index_hobbies_on_person_id (person_id)
35
+ ].join(" ")
36
+ }
37
+
38
+ def mysql(query, options = {})
39
+ _user = options[:user] || USER
40
+ _db = options[:database]
41
+
42
+ puts ""
43
+
44
+ begin
45
+ sh %Q|mysql -u #{_user} #{_db} -e "#{query}"|
46
+ rescue => e
47
+ puts e.message
48
+ end
49
+ end
50
+
51
+ desc "create user for active_record_handler_socket"
52
+ task :create_user do
53
+ mysql "GRANT ALL PRIVILEGES ON *.* TO '#{USER}'@'localhost' WITH GRANT OPTION", :user => "root"
54
+ mysql "SHOW GRANTS FOR 'rails'@'localhost'", :user => "root"
55
+ end
56
+
57
+ desc "create databases for active_record_handler_socket"
58
+ task :create_databases do
59
+ DATABASES.each do |database|
60
+ mysql "CREATE DATABASE #{database} DEFAULT CHARACTER SET 'utf8'"
61
+ end
62
+
63
+ mysql "SHOW DATABASES"
64
+ end
65
+
66
+ desc "create tables for active_record_handler_socket"
67
+ task :create_tables do
68
+ DATABASES.each do |database|
69
+ TABLES.each do |table, schema|
70
+ mysql "CREATE TABLE #{table} (#{schema}) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8", :database => database
71
+ end
72
+
73
+ mysql "SHOW TABLES", :database => database
74
+ end
75
+ end
76
+
77
+ desc "run db tasks}"
78
+ task :prepare do
79
+ %W[
80
+ db:create_user
81
+ db:create_databases
82
+ db:create_tables
83
+ ].each do |task|
84
+ Rake::Task[task].invoke
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "active_record_handlersocket/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "active_record_handlersocket"
7
+ s.version = ActiveRecordHandlersocket::VERSION
8
+ s.authors = ["Takayuki Sugita"]
9
+ s.email = ["sugilog@gmail.com"]
10
+ s.homepage = "https://github.com/sugilog/active_record_handlersocket"
11
+ s.summary = %q{HandlerSocket for ActiveRecord}
12
+ s.description = %q{Easy-to-use handlersocket from existing ActiveRecord Models}
13
+
14
+ s.files = `git ls-files`.split("\n").select{|f| f !~ /^examples\// }
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ # specify any dependencies here; for example:
20
+ s.add_runtime_dependency "activerecord", "~> 2.3.12"
21
+ s.add_runtime_dependency "handlersocket", "~> 0.0.2"
22
+ s.add_development_dependency 'rake', '~> 0.9.2.2'
23
+
24
+ if RUBY_VERSION >= "1.8.7"
25
+ s.add_development_dependency "mysql2"
26
+ s.add_development_dependency "rspec"
27
+ s.add_development_dependency "factory_girl"
28
+ s.add_development_dependency "database_cleaner"
29
+ else
30
+ s.add_development_dependency "mysql2", '0.2.18'
31
+ s.add_development_dependency "rspec", "~> 2.11.0"
32
+ s.add_development_dependency "factory_girl", "2.3.2"
33
+ s.add_development_dependency "database_cleaner", "0.9.1"
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ require 'active_record'
2
+ require 'handlersocket'
3
+
4
+ %W[
5
+ manager
6
+ finder
7
+ connection
8
+ active_record_handler_socket
9
+ base
10
+ ].each do |file|
11
+ require File.join("active_record_handlersocket", file)
12
+ end
13
+
@@ -0,0 +1,15 @@
1
+ module ActiveRecordHandlerSocket
2
+ def self.included(c)
3
+ [
4
+ Manager,
5
+ Finder,
6
+ Connection
7
+ ].each do |_module|
8
+ c.extend _module
9
+ c.extend _module::PrivateMethods
10
+ c.private_class_method *_module::PrivateMethods.instance_methods(false)
11
+ end
12
+
13
+ c.__send__ :hs_establish_connection
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ class Base
3
+ include ActiveRecordHandlerSocket
4
+ end
5
+ end
@@ -0,0 +1,44 @@
1
+ module ActiveRecordHandlerSocket
2
+ module Connection
3
+ def hs_reconnect!
4
+ hs_read_connection.reconnect
5
+ hs_reset_opened_indexes
6
+ hs_active?
7
+ end
8
+
9
+ # XXX: stable_point cannot return correct status before open_index.
10
+ # connection establish with unknown port -> call stable_point -> retrun true
11
+ def hs_active?
12
+ [
13
+ hs_read_connection.stable_point
14
+ ].all?
15
+ end
16
+
17
+ module PrivateMethods
18
+ mattr_reader :hs_connections
19
+ @@hs_connections = {}
20
+
21
+ # TODO: writeread connection
22
+ def hs_establish_connection(name = nil)
23
+ case name
24
+ when nil
25
+ hs_establish_connection("#{RAILS_ENV}_hs_read")
26
+ else
27
+ if config = ActiveRecord::Base.configurations[name]
28
+ config = config.symbolize_keys
29
+
30
+ @@hs_connections.update(
31
+ :read => HandlerSocket.new(:host => config[:host], :port => config[:port].to_s)
32
+ )
33
+ else
34
+ raise ArgumentError, "unknown configuration: #{name}"
35
+ end
36
+ end
37
+ end
38
+
39
+ def hs_read_connection
40
+ @@hs_connections[:read]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,104 @@
1
+ module ActiveRecordHandlerSocket
2
+ class CannotConnectError < StandardError; end
3
+
4
+ module Finder
5
+ def method_missing(method_name, *args, &block)
6
+ case method_name.to_s
7
+ when /^hsfind_(by|multi_by)_([_a-zA-Z]\w*)$/
8
+ finder = :first if $1 == "by"
9
+ finder = :multi if $1 == "multi_by"
10
+ key = $2
11
+ hsfind(finder, key, args)
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def hsfind(finder, key, args)
18
+ index_key = hs_index_key(key)
19
+ setting = hs_fetch_key(index_key)
20
+
21
+ options = args.extract_options!
22
+ id = setting[:id]
23
+ operator = options[:operator] || "="
24
+
25
+ hs_open_index(index_key)
26
+
27
+ case finder
28
+ # XXX: experimental
29
+ when :multi
30
+ _args = args.map{|arg|
31
+ _arg = []
32
+ _arg << setting[:id]
33
+ _arg << operator
34
+ _arg << [arg]
35
+ _arg << options[:limit] if options[:limit]
36
+ _arg
37
+ }
38
+
39
+ results = hs_read_connection.execute_multi(_args)
40
+
41
+ results.map{|result|
42
+ hs_instantiate(index_key, result)
43
+ }.flatten
44
+ when :first
45
+ result = hs_read_connection.execute_single(setting[:id], operator, args)
46
+ hs_instantiate(index_key, result).first
47
+ else
48
+ # XXX: Not Support
49
+ end
50
+ end
51
+
52
+ module PrivateMethods
53
+ def hs_open_index(index_key)
54
+ setting = hs_fetch_key(index_key)
55
+
56
+ if setting[:opened]
57
+ return
58
+ end
59
+
60
+ config = configurations["#{RAILS_ENV}_hs_read"].symbolize_keys
61
+
62
+ id = setting[:id]
63
+ database = config[:database]
64
+ table = table_name
65
+ index = setting[:index]
66
+ fields = setting[:fields].join(",")
67
+
68
+ signal = hs_read_connection.open_index(id, database, table, index, fields)
69
+
70
+ case
71
+ when signal == 0
72
+ setting[:opened] = true
73
+ when signal > 0
74
+ error = hs_read_connection.error
75
+ raise ArgumentError, "invalid setting given: #{error}"
76
+ else
77
+ hs_reset_opened_indexes
78
+ error = hs_read_connection.error
79
+ raise ActiveRecordHandlerSocket::CannotConnectError, "connection lost: #{error}"
80
+ end
81
+ end
82
+
83
+ def hs_instantiate(index_key, result_on_single)
84
+ signal, result = result_on_single
85
+
86
+ case
87
+ when signal == 0
88
+ setting = hs_fetch_key(index_key)
89
+ fields = setting[:fields]
90
+
91
+ result.map do |record|
92
+ attrs = Hash[ *fields.zip(record).flatten ]
93
+ instantiate(attrs)
94
+ end
95
+ when signal > 0
96
+ raise ArgumentError, "invalid argument given: #{result}"
97
+ else
98
+ hs_reset_opened_indexes
99
+ raise ActiveRecordHandlerSocket::CannotConnectError, result
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveRecordHandlerSocket
2
+ class UnknownIndexError < StandardError; end
3
+
4
+ module Manager
5
+ module PrivateMethods
6
+ mattr_reader :hs_indexes, :hs_index_count_cache
7
+ @@hs_indexes = {}
8
+ @@hs_index_count_cache = 0
9
+
10
+ def handlersocket(key, index, fields)
11
+ index_key = hs_index_key(key)
12
+
13
+ if @@hs_indexes.has_key?(index_key)
14
+ warn "#{self.name} handlersocket: #{key} was updated"
15
+ end
16
+
17
+ @@hs_indexes.update(
18
+ index_key => {
19
+ :id => hs_index_count,
20
+ :index => index,
21
+ :fields => fields,
22
+ :opened => false
23
+ }
24
+ )
25
+ end
26
+
27
+ def hs_index_key(key)
28
+ [self.name, key].join(":")
29
+ end
30
+
31
+ def hs_fetch_key(index_key)
32
+ @@hs_indexes[index_key] or raise UnknownIndexError, "unknown key given: #{index_key}"
33
+ end
34
+
35
+ def hs_index_count
36
+ @@hs_index_count_cache += 1
37
+ end
38
+
39
+ def hs_reset_opened_indexes
40
+ @@hs_indexes.each do |_, setting|
41
+ setting[:opened] = false
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end