mournful_settings 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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +74 -0
- data/Rakefile +41 -0
- data/lib/mournful_settings/railtie.rb +11 -0
- data/lib/mournful_settings/setting/cipher.rb +43 -0
- data/lib/mournful_settings/setting.rb +106 -0
- data/lib/mournful_settings/version.rb +3 -0
- data/lib/mournful_settings.rb +7 -0
- data/lib/tasks/mournful_settings.rake +28 -0
- data/test/dummy/db/development.sqlite3.db +0 -0
- data/test/dummy/db/test.sqlite3.db +0 -0
- data/test/dummy/lib/setting.rb +5 -0
- data/test/dummy/test/setting_test.rb +118 -0
- data/test/test_helper.rb +6 -0
- metadata +97 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 Rob Nichols
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
= Mournful Settings
|
2
|
+
|
3
|
+
Adds a settings class to a rails app. The settings are mournful because
|
4
|
+
they can be stored encrypted. Aren't puns wonderful.
|
5
|
+
|
6
|
+
== Installation
|
7
|
+
|
8
|
+
gem mournful_settings
|
9
|
+
|
10
|
+
Setting are stored in a database table 'mournful_settings_settings'. To add
|
11
|
+
mournful_settings migrations to the host app run this rake task:
|
12
|
+
|
13
|
+
rake mournful_settings:install:migrations
|
14
|
+
|
15
|
+
Then run 'rake db:migrate' to create the 'mournful_settings_settings' table
|
16
|
+
|
17
|
+
== Usage
|
18
|
+
|
19
|
+
In the host rails app, create a class you wish to use as the object to hold
|
20
|
+
settings, and have it inherit from MournfulSettings::Setting. For example
|
21
|
+
(/app/models/settings.rb)
|
22
|
+
|
23
|
+
class Setting < MournfulSettings::Setting
|
24
|
+
end
|
25
|
+
|
26
|
+
=== Fields
|
27
|
+
Each setting has five fields:
|
28
|
+
|
29
|
+
[name] Identifies the setting. Used in 'for' (see below)
|
30
|
+
|
31
|
+
[value] The value being stored.
|
32
|
+
|
33
|
+
[value_type] Values are stored as strings. value_type defines how that string
|
34
|
+
should be presented. For example, '1.23' with value_type 'number'
|
35
|
+
will be presented as numeric 1.23. If the value_type was 'text'
|
36
|
+
the value returned would be '1.23'.
|
37
|
+
|
38
|
+
[description] Information about the setting being stored
|
39
|
+
|
40
|
+
[encrypted] Boolean: If set to true, the value will be stored in an encrypted
|
41
|
+
format. Otherwise the value will be stored as plain text.
|
42
|
+
|
43
|
+
=== Retrieving a setting
|
44
|
+
|
45
|
+
To use a stored setting, use the 'for' class method:
|
46
|
+
|
47
|
+
Setting.create(:name => 'pi', :value => '3.14159', :value_type => 'number')
|
48
|
+
|
49
|
+
Setting.for(:pi) --> 3.14159
|
50
|
+
|
51
|
+
== Encryption
|
52
|
+
|
53
|
+
By default mournful settings uses a blowfish cipher to encrypt settings, and
|
54
|
+
its own key string.
|
55
|
+
|
56
|
+
=== Set key
|
57
|
+
|
58
|
+
If you wish to use your own encryption key, you can define the key in
|
59
|
+
an initializer, like this:
|
60
|
+
|
61
|
+
Setting::Cipher.key = 'your key'
|
62
|
+
|
63
|
+
=== Change cipher
|
64
|
+
|
65
|
+
Mournful settings uses Ruby's OpenSSL::Cipher. If you wish to use to change
|
66
|
+
the cipher from blowfish, you can alter it like this:
|
67
|
+
|
68
|
+
Setting::Cipher.config = 'aes-128-cbc'
|
69
|
+
|
70
|
+
To see a list of the available options use:
|
71
|
+
|
72
|
+
puts OpenSSL::Cipher.ciphers
|
73
|
+
|
74
|
+
See: http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rdoc/task'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
Rake::RDocTask.new do |rdoc|
|
9
|
+
files =['README.rdoc', 'MIT-LICENSE', 'lib/**/*.rb']
|
10
|
+
rdoc.rdoc_files.add(files)
|
11
|
+
rdoc.main = "README.rdoc" # page to start on
|
12
|
+
rdoc.title = "Dibber Docs"
|
13
|
+
rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
|
14
|
+
rdoc.options << '--line-numbers'
|
15
|
+
end
|
16
|
+
|
17
|
+
Rake::TestTask.new do |t|
|
18
|
+
t.test_files = FileList['test/**/*.rb']
|
19
|
+
end
|
20
|
+
|
21
|
+
namespace :mournful_settings do
|
22
|
+
|
23
|
+
namespace :db do
|
24
|
+
task :environment do
|
25
|
+
require 'active_record'
|
26
|
+
environment = ENV['RAILS_ENV'] || 'development'
|
27
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => "test/dummy/db/#{environment}.sqlite3.db"
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Migrate the database"
|
31
|
+
task(:migrate => :environment) do
|
32
|
+
ActiveRecord::Migrator.migrate("db/migrate", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Roll back the database"
|
36
|
+
task(:rollback => :environment) do
|
37
|
+
ActiveRecord::Migrator.rollback("db/migrate")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'mournful_settings'
|
2
|
+
require 'rails'
|
3
|
+
module MournfulSettings
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
# makes mournful_settings rake tasks available to host app
|
6
|
+
rake_tasks do
|
7
|
+
Dir[File.join(File.dirname(__FILE__),'../tasks/*.rake')].each { |f| load f }
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module MournfulSettings
|
2
|
+
class Setting < ActiveRecord::Base
|
3
|
+
|
4
|
+
# Based on http://philtoland.com/post/807114394/simple-blowfish-encryption-with-ruby
|
5
|
+
module Cipher
|
6
|
+
def self.cipher(mode, data)
|
7
|
+
cipher = OpenSSL::Cipher::Cipher.new(config).send(mode)
|
8
|
+
cipher.key = Digest::SHA256.digest(key)
|
9
|
+
cipher.update(data) << cipher.final
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.encrypt(data)
|
13
|
+
cipher(:encrypt, data)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.decrypt(text)
|
17
|
+
cipher(:decrypt, text)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.key=(text)
|
21
|
+
@key = text
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.key
|
25
|
+
@key ||= 'Set your own with Setting::Cipher.key = your_key'
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.config=(text)
|
29
|
+
raise "'#{text}' is not a value cipher" unless OpenSSL::Cipher::Cipher.ciphers.include?(text)
|
30
|
+
@config = text
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.config
|
34
|
+
@config ||= blowfish_cipher
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.blowfish_cipher
|
38
|
+
'bf-cbc'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require_relative 'setting/cipher'
|
3
|
+
module MournfulSettings
|
4
|
+
class Setting < ActiveRecord::Base
|
5
|
+
|
6
|
+
self.table_name = 'mournful_settings_settings'
|
7
|
+
|
8
|
+
VALUE_TYPES = ['text', 'number', 'decimal']
|
9
|
+
|
10
|
+
before_save :encrypt_value
|
11
|
+
|
12
|
+
validates :value_type, :presence => true, :inclusion => {:in => VALUE_TYPES}
|
13
|
+
validates :value, :presence => true
|
14
|
+
validates :name, :uniqueness => true, :presence => true
|
15
|
+
|
16
|
+
def self.value_types
|
17
|
+
VALUE_TYPES
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.for(name)
|
21
|
+
setting = find_by_name(name)
|
22
|
+
setting.value if setting
|
23
|
+
end
|
24
|
+
|
25
|
+
def value
|
26
|
+
if value_type.present?
|
27
|
+
parent_value = encrypted? ? decrypt(super) : super
|
28
|
+
|
29
|
+
case value_type.to_s
|
30
|
+
when 'number'
|
31
|
+
parent_value.to_f
|
32
|
+
when 'decimal'
|
33
|
+
BigDecimal.new(parent_value.to_s)
|
34
|
+
else
|
35
|
+
parent_value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def encrypt(text)
|
42
|
+
add_separators Base64.encode64 Cipher.encrypt text.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
def decrypt(text)
|
46
|
+
if is_encrypted?(text)
|
47
|
+
Cipher.decrypt Base64.decode64 remove_separators text
|
48
|
+
else
|
49
|
+
text
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def is_encrypted?(text)
|
54
|
+
inside_separators_and_is_base64_encoded?(text)
|
55
|
+
end
|
56
|
+
|
57
|
+
def inside_separators_and_is_base64_encoded?(text)
|
58
|
+
return unless text.kind_of? String
|
59
|
+
bytes = text.bytes.to_a
|
60
|
+
return unless bytes[0] == separator_byte
|
61
|
+
return unless bytes[-1] == separator_byte
|
62
|
+
return unless bytes[-2] == last_byte_of_base_64_encoded_text
|
63
|
+
non_white_space_with_equal_sign_packing =~ text[1..-3]
|
64
|
+
end
|
65
|
+
|
66
|
+
def non_white_space_with_equal_sign_packing
|
67
|
+
/\S+=*/
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def encrypt_value
|
72
|
+
if encrypted?
|
73
|
+
self.value = encrypt(self.value)
|
74
|
+
else
|
75
|
+
self.value = decrypt(self.value)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_separators(text)
|
80
|
+
[separator, text, separator].join
|
81
|
+
end
|
82
|
+
|
83
|
+
def remove_separators(text)
|
84
|
+
text.gsub(separator, "")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Used to delimit encrypted values to make identification more reliable
|
88
|
+
def separator
|
89
|
+
separator_byte.chr
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def separator_byte
|
94
|
+
31 # ASCII unit separator
|
95
|
+
end
|
96
|
+
|
97
|
+
def last_byte_of_base_64_encoded_text
|
98
|
+
line_feed_byte
|
99
|
+
end
|
100
|
+
|
101
|
+
def line_feed_byte
|
102
|
+
10
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
namespace :mournful_settings do
|
2
|
+
|
3
|
+
desc 'Outputs a mournful test message'
|
4
|
+
task(:task_test => :environment) do
|
5
|
+
puts "Able to access mournful tasks located at #{File.dirname(__FILE__)}"
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :install do
|
9
|
+
# TODO - register within 'rake railties:install:migrations'
|
10
|
+
desc 'Copies mournful_settings migrations to host rails app'
|
11
|
+
task(:migrations => :environment) do
|
12
|
+
mournful_migrate_path = File.expand_path("../../db/migrate", File.dirname(__FILE__))
|
13
|
+
rails_migrate_path = File.expand_path("db/migrate", Rails.root)
|
14
|
+
scope = :mournful_settings
|
15
|
+
migration = ActiveRecord::Migration.new
|
16
|
+
output = migration.copy rails_migrate_path, {scope => mournful_migrate_path}
|
17
|
+
if output.empty?
|
18
|
+
puts "No migrations copied to #{rails_migrate_path}"
|
19
|
+
else
|
20
|
+
puts "Migrations created at #{rails_migrate_path}:"
|
21
|
+
files = output.collect{|m| m.filename.sub rails_migrate_path, ""}
|
22
|
+
files.each{|m| puts "\t#{m}"}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require_relative '../../test_helper'
|
2
|
+
|
3
|
+
class SettingTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@value = 'A secret'
|
7
|
+
Setting::Cipher.config = 'aes-128-cbc'
|
8
|
+
Setting::Cipher.key = 'something else'
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
Setting.delete_all
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_inheritence
|
16
|
+
assert_kind_of(MournfulSettings::Setting, text_setting)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_number_value
|
20
|
+
assert_kind_of(Float, number_setting.value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_text_value
|
24
|
+
assert_kind_of(String, text_setting.value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_decimal_value
|
28
|
+
assert_kind_of(BigDecimal, decimal_setting.value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_encrypted_value
|
32
|
+
assert_kind_of(String, encrypted_setting.value)
|
33
|
+
assert_equal(@value, encrypted_setting.value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_encrypted_value_is_encrypted_in_database
|
37
|
+
database_value = database_value_for(encrypted_setting)
|
38
|
+
assert_not_equal(database_value, encrypted_setting.value)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_encrypted_with_different_value_types
|
42
|
+
{
|
43
|
+
'text' => 'this is a load of text',
|
44
|
+
'number' => 1.33333333,
|
45
|
+
'decimal' => 1.44
|
46
|
+
}.each do |value_type, value|
|
47
|
+
setting = Setting.create(:name => value_type, :value => value, :value_type => value_type, :encrypted => true)
|
48
|
+
assert_equal(value, setting.value)
|
49
|
+
assert_not_equal(database_value_for(setting), setting.value)
|
50
|
+
assert_not_equal(database_value_for(setting).to_s, setting.value.to_s)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_encrypting_an_existing_setting
|
55
|
+
value = number_setting.value
|
56
|
+
number_setting.encrypted = true
|
57
|
+
assert number_setting.save, "Should be able to save a setting after changing it to encrypted"
|
58
|
+
assert_equal(value, number_setting.value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_unencrypting_an_encrypted_setting
|
62
|
+
encrypted_setting.encrypted = false
|
63
|
+
assert encrypted_setting.save, "Should be able to save a setting after changing it to unencrypted"
|
64
|
+
assert_equal(@value, encrypted_setting.value)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_valid_types
|
68
|
+
Setting::VALUE_TYPES.each do |valid_type|
|
69
|
+
number_setting.value_type = valid_type
|
70
|
+
assert(number_setting.valid?, "number_setting should be valid when value_type = #{valid_type}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_invalid_type
|
75
|
+
number_setting.value_type = 'invalid'
|
76
|
+
assert(number_setting.invalid?, "number_setting should be invalid")
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_for
|
80
|
+
[number_setting, text_setting, decimal_setting, encrypted_setting].each do |setting|
|
81
|
+
assert_equal(setting.value, Setting.for(setting.name.to_sym))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_for_when_no_matching_setting
|
86
|
+
assert_nil(Setting.for(:nothing), "Should return nil when setting doesn't exist")
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_setting_an_invalid_cipher_config
|
90
|
+
assert_raises RuntimeError do
|
91
|
+
Setting::Cipher.config = 'invalid'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def text_setting
|
97
|
+
@text_setting ||= Setting.create(:name => 'text_setting', :value => 'foo', :value_type => 'text')
|
98
|
+
end
|
99
|
+
|
100
|
+
def number_setting
|
101
|
+
@number_setting ||= Setting.create(:name => 'number_setting', :value => '1.33333333333333', :value_type => 'number')
|
102
|
+
end
|
103
|
+
|
104
|
+
def decimal_setting
|
105
|
+
@decimal_setting ||= Setting.create(:name => 'decimal_setting', :value => '4.55', :value_type => 'decimal')
|
106
|
+
end
|
107
|
+
|
108
|
+
def encrypted_setting
|
109
|
+
@encrypted_setting ||= Setting.create(:name => 'encrypted_setting', :value => @value, :value_type => 'text', :encrypted => true)
|
110
|
+
end
|
111
|
+
|
112
|
+
def database_value_for(setting)
|
113
|
+
sql = "SELECT value FROM mournful_settings_settings WHERE id = #{setting.id}"
|
114
|
+
Setting.connection.select_value(sql)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mournful_settings
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rob Nichols
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
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: sqlite3
|
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
|
+
description: Packages up code needed to pull data from YAML files when seeding, and
|
47
|
+
adds a process log.
|
48
|
+
email:
|
49
|
+
- rob@undervale.co.uk
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- lib/mournful_settings/version.rb
|
55
|
+
- lib/mournful_settings/setting/cipher.rb
|
56
|
+
- lib/mournful_settings/setting.rb
|
57
|
+
- lib/mournful_settings/railtie.rb
|
58
|
+
- lib/mournful_settings.rb
|
59
|
+
- lib/tasks/mournful_settings.rake
|
60
|
+
- MIT-LICENSE
|
61
|
+
- Rakefile
|
62
|
+
- README.rdoc
|
63
|
+
- test/dummy/lib/setting.rb
|
64
|
+
- test/dummy/db/development.sqlite3.db
|
65
|
+
- test/dummy/db/test.sqlite3.db
|
66
|
+
- test/dummy/test/setting_test.rb
|
67
|
+
- test/test_helper.rb
|
68
|
+
homepage: https://github.com/reggieb/mournful_settings
|
69
|
+
licenses: []
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.8.24
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Tool for adding encrypted settings to an app.
|
92
|
+
test_files:
|
93
|
+
- test/dummy/lib/setting.rb
|
94
|
+
- test/dummy/db/development.sqlite3.db
|
95
|
+
- test/dummy/db/test.sqlite3.db
|
96
|
+
- test/dummy/test/setting_test.rb
|
97
|
+
- test/test_helper.rb
|