aws-sessionstore-dynamodb 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +28 -0
  5. data/LICENSE.txt +12 -0
  6. data/README.md +171 -0
  7. data/Rakefile +15 -0
  8. data/aws-sessionstore-dynamodb.gemspec +18 -0
  9. data/lib/aws-sessionstore-dynamodb.rb +34 -0
  10. data/lib/aws/session_store/dynamo_db/configuration.rb +298 -0
  11. data/lib/aws/session_store/dynamo_db/errors/base_handler.rb +45 -0
  12. data/lib/aws/session_store/dynamo_db/errors/default_handler.rb +57 -0
  13. data/lib/aws/session_store/dynamo_db/garbage_collection.rb +128 -0
  14. data/lib/aws/session_store/dynamo_db/invalid_id_error.rb +21 -0
  15. data/lib/aws/session_store/dynamo_db/lock_wait_timeout_error.rb +21 -0
  16. data/lib/aws/session_store/dynamo_db/locking/base.rb +162 -0
  17. data/lib/aws/session_store/dynamo_db/locking/null.rb +40 -0
  18. data/lib/aws/session_store/dynamo_db/locking/pessimistic.rb +160 -0
  19. data/lib/aws/session_store/dynamo_db/missing_secret_key_error.rb +21 -0
  20. data/lib/aws/session_store/dynamo_db/rack_middleware.rb +130 -0
  21. data/lib/aws/session_store/dynamo_db/railtie.rb +28 -0
  22. data/lib/aws/session_store/dynamo_db/table.rb +98 -0
  23. data/lib/aws/session_store/dynamo_db/tasks/session_table.rake +21 -0
  24. data/lib/aws/session_store/dynamo_db/version.rb +21 -0
  25. data/lib/rails/generators/sessionstore/dynamodb/dynamodb_generator.rb +55 -0
  26. data/lib/rails/generators/sessionstore/dynamodb/templates/sessionstore/USAGE +13 -0
  27. data/lib/rails/generators/sessionstore/dynamodb/templates/sessionstore/dynamodb.yml +71 -0
  28. data/lib/rails/generators/sessionstore/dynamodb/templates/sessionstore_migration.rb +10 -0
  29. data/spec/aws/session_store/dynamo_db/app_config.yml +19 -0
  30. data/spec/aws/session_store/dynamo_db/config/dynamo_db_session.yml +24 -0
  31. data/spec/aws/session_store/dynamo_db/configuration_spec.rb +101 -0
  32. data/spec/aws/session_store/dynamo_db/error/default_error_handler_spec.rb +62 -0
  33. data/spec/aws/session_store/dynamo_db/garbage_collection_spec.rb +156 -0
  34. data/spec/aws/session_store/dynamo_db/locking/threaded_sessions_spec.rb +95 -0
  35. data/spec/aws/session_store/dynamo_db/rack_middleware_database_spec.rb +129 -0
  36. data/spec/aws/session_store/dynamo_db/rack_middleware_spec.rb +149 -0
  37. data/spec/aws/session_store/dynamo_db/rails_app_config.yml +24 -0
  38. data/spec/aws/session_store/dynamo_db/table_spec.rb +46 -0
  39. data/spec/spec_helper.rb +61 -0
  40. data/tasks/test.rake +29 -0
  41. metadata +123 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1395af7cb7211af4f4cb72dcd4037ee046627a64
4
+ data.tar.gz: 55d5c7eb166a8571b05c883fb05baa53d293152f
5
+ SHA512:
6
+ metadata.gz: a9f754fabdd1af66c5853249a946b39b76eca01db99ee569ba9f63b11eec8ba2be7f93d94f6e8055af7013371dde889d6af3c3e093967aeaa24dd56c9b4b52f3
7
+ data.tar.gz: 3d276410f121bdf18acc8217a14c4cc6967a0cd84863e562aa4dd17e1ce38765d4ee42da3472cc3a9ca75d10cbf20ec6cdac15877e2d7d01d84eac39072dca63
@@ -0,0 +1,4 @@
1
+ doc
2
+ coverage
3
+ .yardoc
4
+ Gemfile.lock
@@ -0,0 +1,3 @@
1
+ --title "Amazon DynamoDB Session Store"
2
+ --exclude /generators/
3
+ --hide-api private
data/Gemfile ADDED
@@ -0,0 +1,28 @@
1
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ source 'https://rubygems.org'
15
+
16
+ gemspec
17
+
18
+ gem 'rake', '~> 10.0'
19
+
20
+ group :documentation do
21
+ gem 'yard', '~> 0.0'
22
+ end
23
+
24
+ group :test do
25
+ gem 'rspec', '~> 2.0'
26
+ gem 'simplecov', '~> 0.0', :require => false
27
+ gem 'rack-test', '~> 0.0'
28
+ end
@@ -0,0 +1,12 @@
1
+ Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ may not use this file except in compliance with the License. A copy of
5
+ the License is located at
6
+
7
+ http://aws.amazon.com/apache2.0/
8
+
9
+ or in the "license" file accompanying this file. This file is
10
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ ANY KIND, either express or implied. See the License for the specific
12
+ language governing permissions and limitations under the License.
@@ -0,0 +1,171 @@
1
+ # Amazon DynamoDB Session Store
2
+
3
+ The **Amazon DynamoDB Session Store** handles sessions for Ruby web applications
4
+ using a DynamoDB backend. The session store is compatible with Rails and other
5
+ Rack based frameworks.
6
+
7
+ ## Installation
8
+
9
+ #### Rails Installation
10
+
11
+ Install the session store gem by placing the following command into your
12
+ Gemfile:
13
+
14
+ gem 'aws-sessionstore-dynamodb'
15
+
16
+ You will need to have an existing Amazon DynamoDB session table in order for the
17
+ application to work. You can generate a migration file for the session table
18
+ with the following command:
19
+
20
+ rails generate sessionstore:dynamodb
21
+
22
+ To create the table, run migrations as normal with:
23
+
24
+ rake db:migrate
25
+
26
+ Change the session store to `:dynamodb_store` by editing
27
+ `config/initializers/session_store.rb` to contain the following:
28
+
29
+ YourAppName::Application.config.session_store :dynamodb_store
30
+
31
+ You can now start your Rails application with session support.
32
+
33
+ #### Basic Rack Application Installation
34
+
35
+ For non-Rails applications, you can create the Amazon DynamoDB table in a
36
+ Ruby file using the following method:
37
+
38
+ require 'aws-sessionstore-dynamodb'
39
+
40
+ AWS::SessionStore::DynamoDB::Table.create_table
41
+
42
+ Run the session store as a Rack middleware in the following way:
43
+
44
+ require 'aws-sessionstore-dynamodb'
45
+ require 'some_rack_app'
46
+
47
+ options = { :secret_key => 'SECRET_KEY' }
48
+
49
+ use AWS::SessionStore::DynamoDB::RackMiddleware.new(options)
50
+ run SomeRackApp
51
+
52
+ Note that `:secret_key` is a mandatory configuration option that must be set.
53
+
54
+ ## Detailed Usage
55
+
56
+ The session store is a Rack Middleware, meaning that it will implement the Rack
57
+ interface for dealing with HTTP request/responses.
58
+
59
+ This session store uses a DynamoDB backend in order to provide scaling and
60
+ centralized data benefits for session storage with more ease than other
61
+ containers, like local servers or cookies. Once an application scales beyond
62
+ a single web server, session data will need to be shared across the servers.
63
+ DynamoDB takes care of this burden for you by scaling with your application.
64
+ Cookie storage places all session data on the client side,
65
+ discouraging sensitive data storage. It also forces strict data size
66
+ limitations. DynamoDB takes care of these concerns by allowing for a safe and
67
+ scalable storage container with a much larger data size limit for session data.
68
+
69
+ Full API documentation of the library can be found on [RubyDoc.info][1].
70
+
71
+ ### Configuration Options
72
+
73
+ A number of options are available to be set in
74
+ `AWS::SessionStore::DynamoDB::Configuration`, which is used by the
75
+ `RackMiddleware` class. These options can be set in the YAML configuration
76
+ file in a Rails application (located in `config/sessionstore/dynamodb.yml`),
77
+ directly by Ruby code, or through environment variables.
78
+
79
+ The full set of options along with defaults can be found in the
80
+ [Configuration class documentation][2].
81
+
82
+ #### Environment Options
83
+
84
+ Certain configuration options can be loaded from the environment. These
85
+ options must be specified in the following format:
86
+
87
+ DYNAMO_DB_SESSION_NAME-OF-CONFIGURATION-OPTION
88
+
89
+ The example below would be a valid way to set the session table name:
90
+
91
+ export DYNAMO_DB_SESSION_TABLE_NAME='sessions'
92
+
93
+ ### Rails Generator Details
94
+
95
+ The generator command specified in the installation section will generate two
96
+ files: a migration file, `db/migration/VERSION_migration_name.rb`, and a
97
+ configuration YAML file, `config/sessionstore/dynamodb.yml`.
98
+
99
+ You can run the command with an argument that will define the name of the
100
+ migration file. Once the YAML file is created, you can uncomment any of the
101
+ lines to set configuration options to your liking. The session store will pull
102
+ options from `config/sessionstore/dynamodb.yml` by default if the file exists.
103
+ If you do not wish to place the configuration YAML file in that location,
104
+ you can also pass in a different file path to pull options from.
105
+
106
+ ### Garbage Collection
107
+
108
+ You may want to delete old sessions from your session table. The
109
+ following examples show how to clear old sessions from your table.
110
+
111
+ #### Rails
112
+
113
+ A Rake task for garbage collection is provided for Rails applications.
114
+ By default sessions do not expire. See `config/sessionstore/dynamodb.yml` to
115
+ configure the max age or stale period of a session. Once you have configured
116
+ those values you can clear the old sessions with:
117
+
118
+ rake dynamo_db:collect_garbage
119
+
120
+ #### Outside of Rails
121
+
122
+ You can create your own Rake task for garbage collection similar to below:
123
+
124
+ require "aws-sessionstore-dynamodb"
125
+
126
+ desc 'Perform Garbage Collection'
127
+ task :garbage_collect do |t|
128
+ options = {:max_age => 3600*24, max_stale => 5*3600 }
129
+ AWS::SessionStore::DynamoDB::GarbageCollection.collect_garbage(options)
130
+ end
131
+
132
+ The above example will clear sessions older than one day or that have been
133
+ stale for longer than an hour.
134
+
135
+ ### Locking Strategy
136
+
137
+ You may want the Session Store to implement the provided pessimistic locking
138
+ strategy if you are concerned about concurrency issues with session accesses.
139
+ By default, locking is not implemented for the session store. You must trigger
140
+ the locking strategy through the configuration of the session store. Pessimistic
141
+ locking, in this case, means that only one read can be made on a session at
142
+ once. While the session is being read by the process with the lock, other
143
+ processes may try to obtain a lock on the same session but will be blocked.
144
+
145
+ Locking is expensive and will drive up costs depending on how it is used.
146
+ Without locking, one read and one write are performed per request for session
147
+ data manipulation. If a locking strategy is implemented, as many as the total
148
+ maximum wait time divided by the lock retry delay writes to the database.
149
+ Keep these considerations in mind if you plan to enable locking.
150
+
151
+ #### Configuration for Locking
152
+
153
+ The following configuration options will allow you to configure the pessimistic
154
+ locking strategy according to your needs:
155
+
156
+ options = {
157
+ :enable_locking => true,
158
+ :lock_expiry_time => 500,
159
+ :lock_retry_delay => 500,
160
+ :lock_max_wait_time => 1
161
+ }
162
+
163
+ ### Error Handling
164
+
165
+ You can pass in your own error handler for raised exceptions or you can allow
166
+ the default error handler to them for you. See the API documentation
167
+ on the {AWS::SessionStore::DynamoDB::Errors::BaseHandler} class for more
168
+ details.
169
+
170
+ [1]: http://rubydoc.org/gems/aws-sessionstore-dynamodb/frames
171
+ [2]: http://rubydoc.org/gems/aws-sessionstore-dynamodb/AWS/SessionStore/DynamoDB/Configuration#initialize-instance_method
@@ -0,0 +1,15 @@
1
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ Dir.glob(File.dirname(__FILE__) + '/tasks/*.rake').each {|file| load file }
15
+ task :default => 'test:unit'
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/lib/aws/session_store/dynamo_db/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "aws-sessionstore-dynamodb"
5
+ spec.version = AWS::SessionStore::DynamoDB::VERSION
6
+ spec.authors = ["Ruby Robinson"]
7
+ spec.summary = "The Amazon DynamoDB Session Store handles sessions " +
8
+ "for Ruby web applications using a DynamoDB backend."
9
+ spec.homepage = "http://github.com/aws/aws-sessionstore-dynamodb-ruby"
10
+ spec.license = "Apache License 2.0"
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
14
+ spec.require_paths = ["lib"]
15
+
16
+ spec.add_dependency 'aws-sdk', '~> 1.0'
17
+ spec.add_dependency 'rack', '~> 1.0'
18
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+
15
+ module AWS
16
+ module SessionStore
17
+ module DynamoDB; end
18
+ end
19
+ end
20
+
21
+ require 'aws/session_store/dynamo_db/configuration'
22
+ require 'aws/session_store/dynamo_db/invalid_id_error'
23
+ require 'aws/session_store/dynamo_db/missing_secret_key_error'
24
+ require 'aws/session_store/dynamo_db/lock_wait_timeout_error'
25
+ require 'aws/session_store/dynamo_db/errors/base_handler'
26
+ require 'aws/session_store/dynamo_db/errors/default_handler'
27
+ require 'aws/session_store/dynamo_db/garbage_collection'
28
+ require 'aws/session_store/dynamo_db/locking/base'
29
+ require 'aws/session_store/dynamo_db/locking/null'
30
+ require 'aws/session_store/dynamo_db/locking/pessimistic'
31
+ require 'aws/session_store/dynamo_db/rack_middleware'
32
+ require 'aws/session_store/dynamo_db/table'
33
+ require 'aws/session_store/dynamo_db/version'
34
+ require 'aws/session_store/dynamo_db/railtie' if defined?(Rails)
@@ -0,0 +1,298 @@
1
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'yaml'
15
+ require 'aws-sdk'
16
+
17
+ module AWS::SessionStore::DynamoDB
18
+ # This class provides a Configuration object for all DynamoDB transactions
19
+ # by pulling configuration options from Runtime, a YAML file, the ENV and
20
+ # default settings.
21
+ #
22
+ # == Environment Variables
23
+ # The Configuration object can load default values from your environment. An example
24
+ # of setting and environment variable is below:
25
+ #
26
+ # export DYNAMO_DB_SESSION_TABLE_NAME='Sessions'
27
+ #
28
+ # == Handling Errors
29
+ # There are two configurable options for error handling: :raise_errors and :error_handler.
30
+ #
31
+ # If you would like to use the Default Error Handler, you can decide to set :raise_errors
32
+ # to true or false depending on whether you want all errors, regadless of class, to be raised
33
+ # up the stack and essentially throw a 500.
34
+ #
35
+ # If you decide to use your own Error Handler. You may pass it in for the value of the key
36
+ # :error_handler as a cofniguration object. You must implement the BaseErrorHandler class.
37
+ # @see BaseHandler Interface for Error Handling for DynamoDB Session Store.
38
+ #
39
+ # == Locking Strategy
40
+ # By default, locking is not implemented for the session store. You must trigger the
41
+ # locking strategy through the configuration of the session store. Pessimistic locking,
42
+ # in this case, means that only one read can be made on a session at once. While the session
43
+ # is being read by the process with the lock, other processes may try to obtain a lock on
44
+ # the same session but will be blocked. See the accessors with lock in their name for
45
+ # how to configure the pessimistic locking strategy to your needs.
46
+ #
47
+ # == DynamoDB Specific Options
48
+ # You may configure the table name and table hash key value of your session table with
49
+ # the :table_name and :table_key options. You may also configure performance options for
50
+ # your table with the :consistent_read, :read_capacity, write_capacity. For more information
51
+ # about these configurations see CreateTable method for Amazon DynamoDB.
52
+ #
53
+ class Configuration
54
+
55
+ # Default configuration options
56
+ DEFAULTS = {
57
+ :table_name => "sessions",
58
+ :table_key => "session_id",
59
+ :consistent_read => true,
60
+ :read_capacity => 10,
61
+ :write_capacity => 5,
62
+ :raise_errors => false,
63
+ # :max_age => 7*3600*24,
64
+ # :max_stale => 3600*5,
65
+ :enable_locking => false,
66
+ :lock_expiry_time => 500,
67
+ :lock_retry_delay => 500,
68
+ :lock_max_wait_time => 1,
69
+ :secret_key => nil,
70
+ :api_version => '2012-08-10'
71
+ }
72
+
73
+ # @return [String] Session table name.
74
+ attr_reader :table_name
75
+
76
+ # @return [String] Session table hash key name.
77
+ attr_reader :table_key
78
+
79
+ # @return [true] If a strongly consistent read is used
80
+ # @return [false] If an eventually consistent read is used.
81
+ # See AWS DynamoDB documentation for table consistent_read for more
82
+ # information on this setting.
83
+ attr_reader :consistent_read
84
+
85
+ # @return [Integer] Maximum number of reads consumed per second before
86
+ # DynamoDB returns a ThrottlingException. See AWS DynamoDB documentation
87
+ # for table read_capacity for more information on this setting.
88
+ attr_reader :read_capacity
89
+
90
+ # @return [Integer] Maximum number of writes consumed per second before
91
+ # DynamoDB returns a ThrottlingException. See AWS DynamoDB documentation
92
+ # for table write_capacity for more information on this setting.
93
+ attr_reader :write_capacity
94
+
95
+ # @return [true] All errors are raised up the stack when default ErrorHandler
96
+ # is used.
97
+ # @return [false] Only specified errors are raised up the stack when default
98
+ # ErrorHandler is used.
99
+ attr_reader :raise_errors
100
+
101
+ # @return [DynamoDB Client] DynamoDB client.
102
+ attr_reader :dynamo_db_client
103
+
104
+ # @return [Error Handler] An error handling object that handles all exceptions
105
+ # thrown during execution of the AWS DynamoDB Session Store Rack Middleware.
106
+ # For more information see the Handling Errors Section.
107
+ attr_reader :error_handler
108
+
109
+ # @return [Integer] Maximum number of seconds earlier
110
+ # from the current time that a session was created.
111
+ attr_reader :max_age
112
+
113
+ # @return [Integer] Maximum number of seconds
114
+ # before the current time that the session was last accessed.
115
+ attr_reader :max_stale
116
+
117
+ # @return [String] The secret key for HMAC encryption.
118
+ attr_reader :secret_key
119
+
120
+ # @return [true] Pessimistic locking strategy will be implemented for
121
+ # all session accesses.
122
+ # @return [false] No locking strategy will be implemented for
123
+ # all session accesses.
124
+ attr_reader :enable_locking
125
+
126
+ # @return [Integer] Time in milleseconds after which lock will expire.
127
+ attr_reader :lock_expiry_time
128
+
129
+ # @return [Integer] Time in milleseconds to wait before retrying to obtain
130
+ # lock once an attempt to obtain lock has been made and has failed.
131
+ attr_reader :lock_retry_delay
132
+
133
+ # @return [Integer] Maximum time in seconds to wait to acquire lock
134
+ # before giving up.
135
+ attr_reader :lock_max_wait_time
136
+
137
+
138
+ # Provides configuration object that allows access to options defined
139
+ # during Runtime, in a YAML file, in the ENV and by default.
140
+ #
141
+ # @option options [String] :table_name ("Sessions") Name of the session
142
+ # table.
143
+ # @option options [String] :table_key ("id") The hash key of the sesison
144
+ # table.
145
+ # @option options [Boolean] :consistent_read (true) If true, a strongly
146
+ # consistent read is used. If false, an eventually consistent read is
147
+ # used.
148
+ # @option options [Integer] :read_capacity (10) The maximum number of
149
+ # strongly consistent reads consumed per second before
150
+ # DynamoDB raises a ThrottlingException. See AWS DynamoDB documentation
151
+ # for table read_capacity for more information on this setting.
152
+ # @option options [Integer] :write_capacity (5) The maximum number of writes
153
+ # consumed per second before DynamoDB returns a ThrottlingException.
154
+ # See AWS DynamoDB documentation for table write_capacity for more
155
+ # information on this setting.
156
+ # @option options [DynamoDB Client] :dynamo_db_client
157
+ # (AWS::DynamoDB::ClientV2) DynamoDB client used to perform database
158
+ # operations inside of middleware application.
159
+ # @option options [Boolean] :raise_errors (false) If true, all errors are
160
+ # raised up the stack when default ErrorHandler. If false, Only specified
161
+ # errors are raised up the stack when default ErrorHandler is used.
162
+ # @option options [Error Handler] :error_handler (DefaultErrorHandler)
163
+ # An error handling object that handles all exceptions thrown during
164
+ # execution of the AWS DynamoDB Session Store Rack Middleware.
165
+ # For more information see the Handling Errors Section.
166
+ # @option options [Integer] :max_age (nil) Maximum number of seconds earlier
167
+ # from the current time that a session was created.
168
+ # @option options [Integer] :max_stale (nil) Maximum number of seconds
169
+ # before current time that session was last accessed.
170
+ # @option options [String] :secret_key (nil) Secret key for HMAC encription.
171
+ # @option options [Integer] :enable_locking (false) If true, a pessimistic
172
+ # locking strategy will be implemented for all session accesses.
173
+ # If false, no locking strategy will be implemented for all session
174
+ # accesses.
175
+ # @option options [Integer] :lock_expiry_time (500) Time in milliseconds
176
+ # after which lock expires on session.
177
+ # @option options [Integer] :lock_retry_delay (500) Time in milleseconds to
178
+ # wait before retrying to obtain lock once an attempt to obtain lock
179
+ # has been made and has failed.
180
+ # @option options [Integer] :lock_max_wait_time (500) Maximum time in seconds
181
+ # to wait to acquire lock before giving up.
182
+ # @option options [String] :secret_key (SecureRandom.hex(64))
183
+ # Secret key for HMAC encription.
184
+ def initialize(options = {})
185
+ @options = default_options.merge(
186
+ env_options.merge(
187
+ file_options(options).merge(
188
+ symbolize_keys(options)
189
+ )
190
+ )
191
+ )
192
+ @options = client_error.merge(@options)
193
+ set_attributes(@options)
194
+ end
195
+
196
+ # @return [Hash] The merged configuration hash.
197
+ def to_hash
198
+ @options.dup
199
+ end
200
+
201
+ private
202
+
203
+ # @return [Hash] DDB client.
204
+ def gen_dynamo_db_client
205
+ client_opts = client_subset(@options)
206
+ client = AWS::DynamoDB::Client
207
+ dynamo_db_client = @options[:dynamo_db_client] || client.new(client_opts)
208
+ {:dynamo_db_client => dynamo_db_client}
209
+ end
210
+
211
+ # @return [Hash] Default Error Handler
212
+ def gen_error_handler
213
+ default_handler = AWS::SessionStore::DynamoDB::Errors::DefaultHandler
214
+ error_handler = @options[:error_handler] ||
215
+ default_handler.new(@options[:raise_errors])
216
+ {:error_handler => error_handler}
217
+ end
218
+
219
+ # @return [Hash] Client and error objects in hash.
220
+ def client_error
221
+ gen_error_handler.merge(gen_dynamo_db_client)
222
+ end
223
+
224
+ # @return [Hash] Default Session table options.
225
+ def default_options
226
+ DEFAULTS
227
+ end
228
+
229
+ # @return [Hash] Environment options that are useful for Session Handler.
230
+ def env_options
231
+ default_options.keys.inject({}) do |opts, opt_name|
232
+ env_var = "DYNAMO_DB_SESSION_#{opt_name.to_s.upcase}"
233
+ opts[opt_name] = ENV[env_var] if ENV.key?(env_var)
234
+ opts
235
+ end
236
+ end
237
+
238
+ # @return [Hash] File options.
239
+ def file_options(options = {})
240
+ file_path = config_file_path(options)
241
+ if file_path
242
+ load_from_file(file_path)
243
+ elsif rails_defined && File.exists?(rails_config_file_path)
244
+ load_from_file(rails_config_file_path)
245
+ else
246
+ {}
247
+ end
248
+ end
249
+
250
+ # @return [Boolean] Necessary Rails variables defined.
251
+ def rails_defined
252
+ defined?(Rails) && defined?(Rails.root) && defined?(Rails.env)
253
+ end
254
+
255
+ # Load options from YAML file depending on existence of Rails
256
+ # and possible development stage defined.
257
+ def load_from_file(file_path)
258
+ require "erb"
259
+ opts = YAML.load(ERB.new(File.read(file_path)).result) || {}
260
+ opts = opts[Rails.env] if rails_defined && opts.key?(Rails.env)
261
+ symbolize_keys(opts)
262
+ end
263
+
264
+ # @return [String] Configuration path found in environment or YAML file.
265
+ def config_file_path(options)
266
+ options[:config_file] || ENV["DYNAMO_DB_SESSION_CONFIG_FILE"]
267
+ end
268
+
269
+ # @return [String] Rails configuraton path to YAML file default.
270
+ def rails_config_file_path
271
+ File.join(Rails.root, "config", "sessionstore/dynamodb.yml")
272
+ end
273
+
274
+ # Set accessible attributes after merged options.
275
+ def set_attributes(options)
276
+ @options.keys.each do |opt_name|
277
+ instance_variable_set("@#{opt_name}", options[opt_name])
278
+ end
279
+ end
280
+
281
+ # @return [Hash] Hash with all symbolized keys.
282
+ def symbolize_keys(options)
283
+ options.inject({}) do |opts, (opt_name, opt_value)|
284
+ opts[opt_name.to_sym] = opt_value
285
+ opts
286
+ end
287
+ end
288
+
289
+ # @return [Hash] Client subset options hash.
290
+ def client_subset(options = {})
291
+ client_keys = [:aws_secret_key, :aws_region, :aws_access_key, :api_version]
292
+ options.inject({}) do |opts, (opt_name, opt_value)|
293
+ opts[opt_name.to_sym] = opt_value if client_keys.include?(opt_name.to_sym)
294
+ opts
295
+ end
296
+ end
297
+ end
298
+ end