database_specification 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,196 @@
1
+ require "database_specification/version"
2
+ require 'uri'
3
+ require 'cgi'
4
+
5
+ # Translate beween different ways of describing a database connection
6
+ # Currently supports
7
+ #
8
+ # - ActiveRecord hash
9
+ # - Sequel hash
10
+ # - URL (Sequel/Heroku)
11
+ #
12
+ # Construct an object with the appropriate from-database constructor,
13
+ # and then use the appopriate to-database accessor.
14
+ class DatabaseSpecification
15
+ # The database driver type: postgres, sqlite, etc.
16
+ attr_reader :adapter
17
+ # Database name within the server
18
+ attr_reader :database
19
+ attr_reader :user
20
+ attr_reader :password
21
+ attr_reader :host
22
+ attr_reader :port
23
+ # Misc extra options, ex: pool, timeout
24
+ attr_reader :options
25
+
26
+ # How we store the information internally
27
+ STANDARD = [
28
+ :adapter,
29
+ :database,
30
+ :user,
31
+ :username,
32
+ :password,
33
+ :host,
34
+ :port,
35
+ ]
36
+
37
+ # ActiveRecord constructor
38
+ # @param [Hash] config ActiveRecord style db hash
39
+ def self.active_record(config)
40
+ new.from_active_record(config)
41
+ end
42
+
43
+ # Sequel hash constructor
44
+ # @param [Hash] config Sequel style db hash
45
+ def self.sequel(config)
46
+ new.from_sequel(config)
47
+ end
48
+
49
+ # URL constructor (Sequel/Heroku)
50
+ # @param [String] url
51
+ def self.url(url)
52
+ new.from_url(url)
53
+ end
54
+
55
+ # Performs the work of setting up from an ActiveRecord style hash
56
+ # @param [Hash] config ActiveRecord style db hash
57
+ # @return [DatabaseSpecification]
58
+ def from_active_record(config)
59
+ @adapter = ar_to_sequel(config[:adapter])
60
+ @database = config[:database]
61
+ @user = config[:username]
62
+ @password = config[:password]
63
+ @host = config[:host]
64
+ @port = config[:port]
65
+ @options = (config.keys - STANDARD).map {|k| [k, config[k]]}
66
+ self
67
+ end
68
+
69
+ # Performs the work of setting up from a Sequel style hash
70
+ # @param [Hash] config Sequel style db hash
71
+ # @return [DatabaseSpecification]
72
+ def from_sequel(config)
73
+ @adapter = config[:adapter]
74
+ @database = config[:database]
75
+ @user = config[:user]
76
+ @password = config[:password]
77
+ @host = config[:host]
78
+ @port = config[:port]
79
+ @options = (config.keys - STANDARD).map {|k| [k, config[k]]}
80
+ self
81
+ end
82
+
83
+ # Performs the work of setging up from a url
84
+ # @param [String] url
85
+ # @return [DatabaseSpecification]
86
+ def from_url(url)
87
+ begin
88
+ uri = URI.parse(url)
89
+ rescue URI::InvalidURIError
90
+ raise "Invalid DATABASE_URL"
91
+ end
92
+ @adapter = uri.scheme
93
+ @database = uri.path.split('/')[1]
94
+ @user = uri.user
95
+ @password = uri.password
96
+ @host = uri.host
97
+ @port = uri.port
98
+ @options = parse_query(uri.query)
99
+
100
+ if @adapter == 'sqlite'
101
+ @database = [@host, @database].join('/')
102
+ @host = nil
103
+ end
104
+ self
105
+ end
106
+
107
+ # @return [Hash] ActiveRecord style hash
108
+ def active_record
109
+ Hash[[
110
+ [:adapter, sequel_to_ar(adapter)],
111
+ [:database, database],
112
+ [:username, user],
113
+ [:password, password],
114
+ [:host, host],
115
+ [:port, port],
116
+ ].select {|x| x.last} + options]
117
+ end
118
+
119
+ # @return [Hash] Sequel style hash
120
+ def sequel
121
+ Hash[[
122
+ [:adapter, adapter],
123
+ [:database, database],
124
+ [:user, user],
125
+ [:password, password],
126
+ [:host, host],
127
+ [:port, port],
128
+ ].select {|x| x.last} + options]
129
+ end
130
+
131
+ # @return [String] DB url with query paramters
132
+ def url
133
+ [url_bare, query].compact.join('?')
134
+ end
135
+
136
+ # @return [String] DB url without query parameters
137
+ def url_bare
138
+ if adapter == 'postgres'
139
+ h = host || 'localhost'
140
+ else
141
+ h = host
142
+ end
143
+ uri = URI::Generic.build({
144
+ :scheme => adapter,
145
+ :userinfo => userinfo,
146
+ :host => h,
147
+ :port => port,
148
+ :path => '/' + database, # wants an absolute component
149
+ }).to_s
150
+ # URI is overly agressive in removing leading /
151
+ if adapter == 'sqlite'
152
+ uri.gsub!(':/', '://')
153
+ end
154
+ uri
155
+ end
156
+
157
+ private
158
+ def ar_to_sequel(adapter)
159
+ case adapter
160
+ when 'sqlite3'; 'sqlite'
161
+ when 'postgresql'; 'postgres'
162
+ else; adapter
163
+ end
164
+ end
165
+
166
+ def sequel_to_ar(adapter)
167
+ case adapter
168
+ when 'sqlite'; 'sqlite3'
169
+ when 'postgres'; 'postgresql'
170
+ else; adapter
171
+ end
172
+ end
173
+
174
+ def userinfo
175
+ if user || password
176
+ [user, password].join(':')
177
+ end
178
+ end
179
+
180
+ def query
181
+ q = options.map do |k,v|
182
+ k.to_s + '=' + v.to_s
183
+ end.join('&')
184
+ q unless q.empty?
185
+ end
186
+
187
+ def parse_query(q)
188
+ CGI.parse(q || '').to_a.map do |k, v|
189
+ n = v.first
190
+ if n.match(/\d+/)
191
+ n = n.to_i
192
+ end
193
+ [k.to_sym, n]
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,4 @@
1
+ class DatabaseSpecification
2
+ # version number
3
+ VERSION = "0.0.1"
4
+ end
@@ -0,0 +1,98 @@
1
+ require 'database_specification'
2
+
3
+ describe DatabaseSpecification do
4
+ let(:ar_sqlite) do
5
+ {
6
+ :adapter => 'sqlite3',
7
+ :database => 'db/development.sqlite3',
8
+ :pool => 5,
9
+ :timeout => 5000,
10
+ }
11
+ end
12
+ let(:sequel_sqlite) do
13
+ {
14
+ :adapter => 'sqlite',
15
+ :database => 'db/development.sqlite3',
16
+ :pool => 5,
17
+ :timeout => 5000,
18
+ }
19
+ end
20
+ let(:url_sqlite) {'sqlite://db/development.sqlite3?pool=5&timeout=5000'}
21
+ let(:url_sqlite_bare) {'sqlite://db/development.sqlite3'}
22
+ let(:ar_postgres) do
23
+ {
24
+ :adapter => 'postgresql',
25
+ :database => 'pacha_development',
26
+ :username => 'user',
27
+ :password => 'pass',
28
+ :host => 'localhost',
29
+ :port => 1111,
30
+ }
31
+ end
32
+ let(:ar_postgres_short) do
33
+ {
34
+ :adapter => 'postgresql',
35
+ :database => 'pacha_development',
36
+ }
37
+ end
38
+ let(:url_postgres_short) {'postgres://localhost/pacha_development'}
39
+ let(:sequel_postgres) do
40
+ {
41
+ :adapter => 'postgres',
42
+ :database => 'pacha_development',
43
+ :user => 'user',
44
+ :password => 'pass',
45
+ :host => 'localhost',
46
+ :port => 1111,
47
+ }
48
+ end
49
+ let(:url_postgres) {'postgres://user:pass@localhost:1111/pacha_development'}
50
+
51
+ describe 'ActiveRecord' do
52
+ describe 'sqlite' do
53
+ subject {DatabaseSpecification.active_record(ar_sqlite)}
54
+ its(:active_record) {should == ar_sqlite}
55
+ its(:sequel) {should == sequel_sqlite}
56
+ its(:url) {should == url_sqlite}
57
+ its(:url_bare) {should == url_sqlite_bare}
58
+ end
59
+ describe 'postgres' do
60
+ subject {DatabaseSpecification.active_record(ar_postgres)}
61
+ its(:active_record) {should == ar_postgres}
62
+ its(:sequel) {should == sequel_postgres}
63
+ its(:url) {should == url_postgres}
64
+ end
65
+ describe 'postgres short' do
66
+ subject {DatabaseSpecification.active_record(ar_postgres_short)}
67
+ its(:url) {should == url_postgres_short}
68
+ end
69
+ end
70
+ describe 'Sequel' do
71
+ describe 'sqlite' do
72
+ subject {DatabaseSpecification.sequel(sequel_sqlite)}
73
+ its(:active_record) {should == ar_sqlite}
74
+ its(:sequel) {should == sequel_sqlite}
75
+ its(:url) {should == url_sqlite}
76
+ end
77
+ describe 'postgres' do
78
+ subject {DatabaseSpecification.sequel(sequel_postgres)}
79
+ its(:active_record) {should == ar_postgres}
80
+ its(:sequel) {should == sequel_postgres}
81
+ its(:url) {should == url_postgres}
82
+ end
83
+ end
84
+ describe 'url' do
85
+ describe 'sqlite' do
86
+ subject {DatabaseSpecification.url(url_sqlite)}
87
+ its(:active_record) {should == ar_sqlite}
88
+ its(:sequel) {should == sequel_sqlite}
89
+ its(:url) {should == url_sqlite}
90
+ end
91
+ describe 'postgres' do
92
+ subject {DatabaseSpecification.url(url_postgres)}
93
+ its(:active_record) {should == ar_postgres}
94
+ its(:sequel) {should == sequel_postgres}
95
+ its(:url) {should == url_postgres}
96
+ end
97
+ end
98
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: database_specification
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Justin Love
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: guard-rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Translate between different ways of specifying a database connection. Translates
63
+ between ActiveRecord-hash, Sequel-hash, and urls used by Sequel and Heroku
64
+ email:
65
+ - git@JustinLove.name
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - lib/database_specification.rb
71
+ - lib/database_specification/version.rb
72
+ - spec/database_specification_spec.rb
73
+ homepage: ''
74
+ licenses: []
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project: database_specification
93
+ rubygems_version: 1.8.24
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Translate between different ways of specifying a database connection - eg
97
+ AR-URL
98
+ test_files:
99
+ - spec/database_specification_spec.rb
100
+ has_rdoc: