makara 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +28 -0
  7. data/CHANGELOG.md +27 -0
  8. data/Gemfile +18 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +278 -0
  11. data/Rakefile +9 -0
  12. data/gemfiles/ar30.gemfile +30 -0
  13. data/gemfiles/ar31.gemfile +29 -0
  14. data/gemfiles/ar32.gemfile +29 -0
  15. data/gemfiles/ar40.gemfile +15 -0
  16. data/gemfiles/ar41.gemfile +15 -0
  17. data/gemfiles/ar42.gemfile +15 -0
  18. data/lib/active_record/connection_adapters/jdbcmysql_makara_adapter.rb +25 -0
  19. data/lib/active_record/connection_adapters/jdbcpostgresql_makara_adapter.rb +25 -0
  20. data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +209 -0
  21. data/lib/active_record/connection_adapters/makara_jdbcmysql_adapter.rb +25 -0
  22. data/lib/active_record/connection_adapters/makara_jdbcpostgresql_adapter.rb +25 -0
  23. data/lib/active_record/connection_adapters/makara_mysql2_adapter.rb +44 -0
  24. data/lib/active_record/connection_adapters/makara_postgresql_adapter.rb +44 -0
  25. data/lib/active_record/connection_adapters/mysql2_makara_adapter.rb +44 -0
  26. data/lib/active_record/connection_adapters/postgresql_makara_adapter.rb +44 -0
  27. data/lib/makara.rb +25 -0
  28. data/lib/makara/cache.rb +53 -0
  29. data/lib/makara/cache/memory_store.rb +28 -0
  30. data/lib/makara/cache/noop_store.rb +15 -0
  31. data/lib/makara/config_parser.rb +200 -0
  32. data/lib/makara/connection_wrapper.rb +170 -0
  33. data/lib/makara/context.rb +46 -0
  34. data/lib/makara/error_handler.rb +39 -0
  35. data/lib/makara/errors/all_connections_blacklisted.rb +13 -0
  36. data/lib/makara/errors/blacklist_connection.rb +14 -0
  37. data/lib/makara/errors/no_connections_available.rb +14 -0
  38. data/lib/makara/logging/logger.rb +23 -0
  39. data/lib/makara/logging/subscriber.rb +38 -0
  40. data/lib/makara/middleware.rb +109 -0
  41. data/lib/makara/pool.rb +188 -0
  42. data/lib/makara/proxy.rb +277 -0
  43. data/lib/makara/railtie.rb +14 -0
  44. data/lib/makara/version.rb +15 -0
  45. data/makara.gemspec +19 -0
  46. data/spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb +92 -0
  47. data/spec/active_record/connection_adapters/makara_abstract_adapter_spec.rb +114 -0
  48. data/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +183 -0
  49. data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +121 -0
  50. data/spec/cache_spec.rb +59 -0
  51. data/spec/config_parser_spec.rb +102 -0
  52. data/spec/connection_wrapper_spec.rb +33 -0
  53. data/spec/context_spec.rb +107 -0
  54. data/spec/middleware_spec.rb +84 -0
  55. data/spec/pool_spec.rb +158 -0
  56. data/spec/proxy_spec.rb +182 -0
  57. data/spec/spec_helper.rb +46 -0
  58. data/spec/support/configurator.rb +13 -0
  59. data/spec/support/deep_dup.rb +12 -0
  60. data/spec/support/mock_objects.rb +67 -0
  61. data/spec/support/mysql2_database.yml +17 -0
  62. data/spec/support/mysql2_database_with_custom_errors.yml +17 -0
  63. data/spec/support/pool_extensions.rb +14 -0
  64. data/spec/support/postgresql_database.yml +13 -0
  65. data/spec/support/proxy_extensions.rb +33 -0
  66. data/spec/support/schema.rb +7 -0
  67. metadata +144 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 43573a40ca103839cfe03e0194c500ab656e03a6
4
+ data.tar.gz: e2baf81e7328bfba4d363992aee0e3308df53705
5
+ SHA512:
6
+ metadata.gz: 27dabc4695a384d29c6c41d410e1604020d8c157cd32341a329bbe9a2208efeb5e4b30708398b97cea37df9d990b602da103f5931e22d34aa73c0922f6a9961e
7
+ data.tar.gz: d8f28626cc7d690d160a86dbc9846a99f6112fa0dba32114e1020103ed0dd1c1276fff2d900c7ca57a0c1b278feac6bed02a604addb83e3e41a70d66800a809d
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .DS_Store
7
+ .byebug_history
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
@@ -0,0 +1 @@
1
+ makara
@@ -0,0 +1 @@
1
+ 2.1.5
@@ -0,0 +1,28 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.1
6
+ - jruby
7
+
8
+ gemfile:
9
+ - gemfiles/ar30.gemfile
10
+ - gemfiles/ar31.gemfile
11
+ - gemfiles/ar32.gemfile
12
+ - gemfiles/ar40.gemfile
13
+ - gemfiles/ar41.gemfile
14
+ - gemfiles/ar42.gemfile
15
+
16
+ sudo: false
17
+
18
+ services:
19
+ - mysql
20
+ - postgresql
21
+
22
+ before_script:
23
+ - mysql -e 'create database makara_test;'
24
+ - psql -c 'create database makara_test;' -U postgres
25
+
26
+ before_install:
27
+ - gem update --system 2.1.11
28
+ - gem --version
@@ -0,0 +1,27 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## 0.3.0rc2 - 2014-08-05
5
+ ### Added
6
+ - allow bypassing of stickiness
7
+
8
+ ## 0.3.0rc2 - 2014-08-05
9
+ ### Added
10
+ - add postgres specific tests.
11
+
12
+ ### Changed
13
+ - change using methods for matchers to be able to monkey patch them
14
+ - follow AR naming conventions for adapter naming
15
+
16
+ ## 0.3.0rc1 - 2014-08-05
17
+ ### Removed
18
+ - removed initial connection logic. If a connection can't be made on startup, an error will be thrown rather than the node getting blacklisted.
19
+
20
+
21
+ ## 0.2.2 - 2014-04-03
22
+ ### Added
23
+ - add logging of makara operations via the Makara::Logger
24
+
25
+ ### Changed
26
+ - begin tracing the series of errors associated with blacklisting rather than just the last. This becomes apparent in error messages.
27
+ - fix Rails.cache usage when full environment is not loaded.
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in makara.gemspec
4
+ gemspec
5
+
6
+ gem 'rake'
7
+ gem 'rspec'
8
+ gem 'timecop'
9
+ gem 'byebug', :platform => :ruby
10
+ gem 'ruby-debug', :platform => :jruby
11
+ gem 'rack', '1.6.0'
12
+
13
+ gem 'mysql2', :platform => :ruby
14
+ gem 'pg', :platform => :ruby
15
+
16
+ gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
17
+ gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
18
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mike Nelson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,278 @@
1
+ # Makara
2
+
3
+ [![Build Status](https://travis-ci.org/taskrabbit/makara.png?branch=master)](https://travis-ci.org/taskrabbit/makara)
4
+ [![Code Climate](https://codeclimate.com/repos/526886a7f3ea00679b00cae6/badges/7905f7a000492a1078f7/gpa.png)](https://codeclimate.com/repos/526886a7f3ea00679b00cae6/feed)
5
+
6
+
7
+ Makara is generic master/slave proxy. It handles the heavy lifting of managing, choosing, blacklisting, and cycling through connections. It comes with an ActiveRecord database adapter implementation.
8
+
9
+ #### Warning:
10
+
11
+ There is a potential performance issue when used alongside certain versions of
12
+ [newrelic/rpm](https://github.com/newrelic/rpm), [Issue #59](https://github.com/taskrabbit/makara/issues/59).
13
+
14
+ > Any newrelic_rpm `< 3.11.2` and `>= 3.7.2` will have the regression.
15
+ > I'd recommend upgrading newrelic_rpm to avoid the problem.
16
+
17
+ ## Installation
18
+
19
+ ```ruby
20
+ gem 'makara', github: 'taskrabbit/makara', tag: 'v0.3.x'
21
+ ```
22
+
23
+ ## Basic Usage
24
+
25
+ If you're only interested in the ActiveRecord database adapter... [here you go.](#activerecord-database-adapter)
26
+
27
+ Makara provides a base proxy class which you should inherit from. Your proxy connection class should implement a `connection_for` instance method which will be provided with an individual configuration and expect a real connection back.
28
+
29
+ ```ruby
30
+ class MyAwesomeSqlProxy < ::Makara::Proxy
31
+ def connection_for(config)
32
+ ::Sql::Client.new(config)
33
+ end
34
+ end
35
+ ```
36
+
37
+ Next, you need to decide which methods are proxied and which methods should be sent to all underlying connections:
38
+
39
+ ```ruby
40
+ # within MyAwesomeSqlProxy
41
+ hijack_method :select, :ping
42
+ send_to_all :connect, :reconnect, :disconnect, :clear_cache
43
+ ```
44
+
45
+ Assuming you don't need to split requests between a master and a slave, you're done. If you do need to, implement the `needs_master?` method:
46
+
47
+ ```ruby
48
+ # within MyAwesomeSqlProxy
49
+ def needs_master?(method_name, args)
50
+ return false if args.empty?
51
+ sql = args.first
52
+ sql !~ /^select/i
53
+ end
54
+ ```
55
+
56
+ This implementation will send any request not like "SELECT..." to a master connection. There are more methods you can override and more control over blacklisting - check out the [makara database adapter](lib/active_record/connection_adapters/makara_abstract_adapter.rb) for examples of advanced usage.
57
+
58
+ ### Config Parsing
59
+
60
+ Makara comes with a config parser which will handle providing subconfigs to the `connection_for` method. Check out the ActiveRecord database.yml example below for more info.
61
+
62
+ ### Context
63
+
64
+ Makara handles stickyness by keeping track of a context (sha). In a multi-instance environment it persists a context in a cache. If Rails is present it will automatically use Rails.cache. You can provide any kind of store as long as it responds to the methods required in [lib/makara/cache.rb](lib/makara/cache.rb).
65
+
66
+ ```ruby
67
+ Makara::Cache.store = MyRedisCacheStore.new
68
+ ```
69
+
70
+ To handle persistence of context across requests in a Rack app, makara provides a middleware. It lays a cookie named `_mkra_ctxt` which contains the current master context. If the next request is executed before the cookie expires, master will be used. If something occurs which naturally requires master on the second request, the context is changed and stored again.
71
+
72
+ #### Changing Context
73
+
74
+ If you need to change the makara context, releasing any stuck connections, all you have to do is:
75
+
76
+ ```ruby
77
+ ctx = Makara::Context.generate # or any unique sha
78
+ Makara::Context.set_current ctx
79
+ ```
80
+
81
+ A context is local to the curent thread of execution. This will allow you to stick to master safely in a single thread
82
+ in systems such as sidekiq, for instance.
83
+
84
+
85
+ ### Forcing Master
86
+
87
+ If you need to force master in your app then you can simply invoke stick_to_master! on your connection:
88
+
89
+ ```ruby
90
+ write_to_cache = true # or false
91
+ proxy.stick_to_master!(write_to_cache)
92
+ ```
93
+
94
+
95
+ ### Skipping the Stickiness
96
+
97
+ If you're using the `sticky: true` configuration and you find yourself in a situation where you need to write information through the proxy but you don't want the context to be stuck to master, you should use a `without_sticking` block:
98
+
99
+ ```ruby
100
+ proxy.without_sticking do
101
+ # do my stuff that would normally cause the proxy to stick to master
102
+ end
103
+ ```
104
+
105
+ ### Logging
106
+
107
+ You can set a logger instance to ::Makara::Logging::Logger.logger and Makara will log how it handles errors at the Proxy level.
108
+
109
+ ```ruby
110
+ Makara::Logging::Logger.logger = ::Logger.new(STDOUT)
111
+ ```
112
+
113
+ ## ActiveRecord Database Adapter
114
+
115
+ So you've found yourself with an ActiveRecord-based project which is starting to get some traffic and you realize 95% of you DB load is from reads. Well you've come to the right spot. Makara is a great solution to break up that load not only between master and slave but potentially multiple masters and/or multiple slaves.
116
+
117
+ By creating a makara database adapter which simply acts as a proxy we avoid any major complexity surrounding specific database implementations. The makara adapter doesn't care if the underlying connection is mysql, postgresql, etc it simply cares about the sql string being executed.
118
+
119
+ ### What goes where?
120
+
121
+ Any `SELECT` statements will execute against your slave(s), anything else will go to master. The only edge case is `SET` operations which are sent to all connections. Execution of specific methods such as `connect!`, `disconnect!`, and `clear_cache!` are invoked on all underlying connections.
122
+
123
+ ### Errors / blacklisting
124
+
125
+ Whenever a node fails an operation due to a connection issue, it is blacklisted for the amount of time specified in your database.yml. After that amount of time has passed, the connection will begin receiving queries again. If all slave nodes are blacklisted, the master connection will begin receiving read queries as if it were a slave. Once all nodes are blacklisted the error is raised to the application and all nodes are whitelisted.
126
+
127
+ ### Database.yml
128
+
129
+ Your database.yml should contain the following structure:
130
+
131
+ ```yml
132
+ production:
133
+ adapter: 'mysql2_makara'
134
+ database: 'MyAppProduction'
135
+ # any other standard AR configurations
136
+
137
+ # add a makara subconfig
138
+ makara:
139
+
140
+ # the following are default values
141
+ blacklist_duration: 5
142
+ master_ttl: 5
143
+ sticky: true
144
+
145
+ # list your connections with the override values (they're merged into the top-level config)
146
+ # be sure to provide the role if master, role is assumed to be a slave if not provided
147
+ connections:
148
+ - role: master
149
+ host: master.sql.host
150
+ - role: slave
151
+ host: slave1.sql.host
152
+ - role: slave
153
+ host: slave2.sql.host
154
+ ```
155
+
156
+ Let's break this down a little bit. At the top level of your config you have the standard `adapter` choice. Currently the available adapters are listed in [lib/active_record/connection_adapters/](lib/active_record/connection_adapters/). They are in the form of `#{db_type}_makara` where db_type is mysql, postgresql, etc.
157
+
158
+ Following the adapter choice is all the standard configurations (host, port, retry, database, username, password, etc). With all the standard configurations provided, you can now provide the makara subconfig.
159
+
160
+ The makara subconfig sets up the proxy with a few of its own options, then provides the connection list. The makara options are:
161
+ * blacklist_duration - the number of seconds a node is blacklisted when a connection failure occurs
162
+ * sticky - if a node should be stuck to once it's used during a specific context
163
+ * master_ttl - how long the master context is persisted. generally, this needs to be longer than any replication lag
164
+ * connection_error_matchers - array of custom error matchers you want to be handled gracefully by Makara (as in, errors matching these regexes will result in blacklisting the connection as opposed to raising directly).
165
+
166
+ Connection definitions contain any extra node-specific configurations. If the node should behave as a master you must provide `role: master`. Any previous configurations can be overridden within a specific node's config. Nodes can also contain weights if you'd like to balance usage based on hardware specifications. Optionally, you can provide a name attribute which will be used in sql logging.
167
+
168
+ ```yml
169
+ connections:
170
+ - role: master
171
+ host: mymaster.sql.host
172
+ blacklist_duration: 0
173
+
174
+ # implicit role: slave
175
+ - host: mybigslave.sql.host
176
+ weight: 8
177
+ name: Big Slave
178
+ - host: mysmallslave.sql.host
179
+ weight: 2
180
+ name: Small Slave
181
+ ```
182
+
183
+ In the previous config the "Big Slave" would receive ~80% of traffic.
184
+
185
+ #### DATABASE_URL
186
+
187
+ Connections may specify a `url` parameter in place of host, username, password, etc.
188
+
189
+ ```yml
190
+ connections:
191
+ - role: master
192
+ blacklist_duration: 0
193
+ url: 'mysql2://db_username:db_password@localhost:3306/db_name'
194
+ ```
195
+
196
+ We recommend, if using environmental variables, to interpolate them via ERb.
197
+
198
+ ```yml
199
+ connections:
200
+ - role: master
201
+ blacklist_duration: 0
202
+ url: <%= ENV['DATABASE_URL_MASTER'] %>
203
+ - role: slave
204
+ url: <%= ENV['DATABASE_URL_SLAVE'] %>
205
+ ```
206
+
207
+ **Important**: *Do NOT use `ENV['DATABASE_URL']`*, as it inteferes with the the database configuration
208
+ initialization and may cause Makara not to complete the configuration. For the moment, it is easier
209
+ to use a different ENV variable than to hook into the database initialization in all the supported
210
+ Rails.
211
+
212
+ For more information on url parsing, as used in
213
+ [ConfigParser](https://github.com/taskrabbit/makara/blob/master/lib/makara/config_parser.rb), see:
214
+
215
+ - 3.0
216
+ [ActiveRecord::Base::ConnectionSpecification.new](https://github.com/rails/rails/blob/3-0-stable/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb#L3-L7)
217
+ - 3.0
218
+ [ActiveRecord::Base::ConnectionSpecification.new](https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb#L3-L7)
219
+ - 3.2
220
+ [ActiveRecord::Base::ConnectionSpecification::Resolver.send(:connection_url_to_hash, url_config[:url])](https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb#L60-L77)
221
+ - 4.0
222
+ [ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.send(:connection_url_to_hash, url_config[:url])](https://github.com/rails/rails/blob/4-0-stable/activerecord/lib/active_record/connection_adapters/connection_specification.rb#L68-L92)
223
+ - [ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig](https://github.com/rails/rails/blob/4-0-stable/activerecord/lib/active_record/connection_handling.rb)
224
+ - 4.1
225
+ [ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash](https://github.com/rails/rails/blob/4-1-stable/activerecord/lib/active_record/connection_adapters/connection_specification.rb#L17-L121)
226
+ - ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(url_config).resolve
227
+ - 4.2
228
+ [ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash](https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/connection_handling.rb#L60-L81)
229
+ - master
230
+ [ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash](https://github.com/rails/rails/blob/97b980b4e61aea3cee429bdee4b2eae2329905cd/activerecord/lib/active_record/connection_handling.rb#L60-L81)
231
+
232
+
233
+
234
+ ## Custom error matchers:
235
+
236
+ To enable Makara to catch and handle custom errors gracefully (blacklist the connection instead of raising directly), you must add your custom matchers to the `connection_error_matchers` setting of your config file, for example:
237
+
238
+ ```yml
239
+ production:
240
+ adapter: 'mysql2_makara'
241
+
242
+ makara:
243
+ blacklist_duration: 5
244
+ connection_error_matchers:
245
+ - !ruby/regexp '/^ActiveRecord::StatementInvalid: Mysql2::Error: Unknown command:/'
246
+ - '/Sql Server Has Gone Away/'
247
+ - 'Mysql2::Error: Duplicate entry'
248
+ ```
249
+
250
+ You can provide strings or regexes. In the case of strings, if they start with `/` and end with `/` they will be converted to regexes when evaluated. Strings that don't start and end with `/` will get evaluated with standard comparison.
251
+
252
+ ## Common Problems / Solutions
253
+
254
+ On occasion your app may deal with a situation where makara is not present during a write but a read should use master. In the generic proxy details above you are encouraged to use `stick_to_master!` to accomplish this. Here's an example:
255
+
256
+ ```ruby
257
+ # some third party creates a resource in your db, slave replication may not have completed yet
258
+ # ...
259
+ # then your app is told to read the resource.
260
+ def handle_request_after_third_party_record_creation
261
+ CreatedResourceClass.connection.stick_to_master!
262
+ CreatedResourceClass.find(params[:id]) # will go to master
263
+ end
264
+ ```
265
+
266
+ Similarly, if you have a third party service which will conduct a generic request against your Rack app, you can force master via a query param:
267
+
268
+ ```ruby
269
+ def send_url_to_third_party
270
+ context = Makara::Context.get_current
271
+ ThirdParty.read_from_here!("http://mysite.com/path/to/resource?_mkra_ctxt=#{context}")
272
+ end
273
+ ```
274
+
275
+ ## Todo
276
+
277
+ * Cookie based cache store?
278
+ * More real world examples
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake'
3
+ require "bundler/gem_tasks"
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = 'spec/**/*_spec.rb'
8
+ end
9
+ task :default => :spec
@@ -0,0 +1,30 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in makara.gemspec
4
+ gemspec :path => '../'
5
+
6
+
7
+ gem 'rake'
8
+ gem 'activerecord', '3.0.17'
9
+ gem 'rspec'
10
+ gem 'rack', '1.6.0'
11
+ gem 'i18n', '~> 0.5.0'
12
+ gem 'mysql2', '0.2.11', :platform => :ruby
13
+ gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
14
+ gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
15
+
16
+
17
+ rmajor, rminor, rpatch = RUBY_VERSION.split(/[^\d]/)[0..2].map(&:to_i)
18
+
19
+ if rmajor == 1 && rminor == 8
20
+ gem 'timecop', '0.5.9'
21
+ else
22
+ gem 'timecop'
23
+ end
24
+
25
+ # 1.8 - 1.9.2
26
+ if rmajor == 1 && (rminor == 8 || (rminor == 9 && rpatch < 3))
27
+ gem 'pg', '0.17.1', :platform => :ruby
28
+ else
29
+ gem 'pg', :platform => :ruby
30
+ end