active_record_handlersocket 0.0.1

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.
@@ -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