entity_storage 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,17 @@
1
+ === 1.0.4 2009-09-23
2
+ * Fixed edge case bug with defaults and accessors with underscores
3
+
4
+ === 1.0.3 2009-09-23
5
+ * updated tests and example docs
6
+
7
+ === 1.0.2 2009-09-21
8
+ * changed method of table discovery for auto table creation
9
+
10
+ === 1.0.1 2009-09-20
11
+ * documentation update
12
+
13
+ === 1.0.0 2009-09-19
14
+
15
+ * 1 major enhancement:
16
+ * Initial release
17
+
data/Manifest.txt ADDED
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/entity_storage.rb
6
+ script/console
7
+ script/destroy
8
+ script/generate
9
+ config/database.yml
10
+ test/test_entity_storage.rb
11
+ test/test_helper.rb
data/README.rdoc ADDED
@@ -0,0 +1,98 @@
1
+ = entity_storage
2
+
3
+ http://github.com/eatenbyagrue/entity_storage
4
+
5
+ == DESCRIPTION:
6
+
7
+ An easy to use Key/Value store for any Ruby on Rails project. Like Memcache, only persistent. Stores config values & application wide state in the database in order to survive server restarts.
8
+
9
+ Designed to allow you to add persistent value storage to any Rails project in about 5 minutes.
10
+
11
+ Additionally, allows users to set a list of default keys that auto-initializes baseline key/value pairs in the database for easy initialization.
12
+
13
+ == SYNOPSIS:
14
+
15
+ You can use the entity store like so:
16
+
17
+ # Get key value.
18
+ e = EntityStore["testkey"]
19
+ e = EntityStore.testkey
20
+ e = EntityStore[:testkey]
21
+
22
+ # sets key named 'happened' to a Time object of now
23
+ EntityStore[:happened] = Time.now
24
+ EntityStore["happened"] = Time.now
25
+ EntityStore.happened = Time.now
26
+
27
+ # find out it's default, even if it's been changed
28
+ e = EntityStore.default(:testkey)
29
+
30
+ # or
31
+ e = EntityStore.defaults[:testkey]
32
+
33
+ # set it back to default
34
+ EntityStore.default!(:testkey)
35
+
36
+ # delete an item
37
+ EntityStore.delete(key)
38
+
39
+ All EntityStorage operations sync immediately with the database, so a server shutdown will not impact stored values.
40
+
41
+ If you access a key that doesn't exist, and is specified in default list, will be initialized and returned. If not in default list, will return nil.
42
+
43
+ Keys can be up to 512 characters in length. Values can be practically any size, and consist of any object. Objects are marshalled back and forth between database.
44
+
45
+ == REQUIREMENTS:
46
+
47
+ Requires ActiveRecord 2.2.3 or above (probably works with earlier versions, but has not been tested.)
48
+
49
+ == INSTALL:
50
+ To install from github:
51
+
52
+ Run the following if you haven't already:
53
+
54
+ gem sources -a http://gems.github.com
55
+
56
+ Install the gem(s):
57
+
58
+ sudo gem install eatenbyagrue-entity_storage
59
+
60
+ Or download the souce above and run, customized for version and environment:
61
+
62
+ sudo gem install PATH/entity_storage.#.#.#.gem
63
+
64
+ Put the following at the bottom of your environment.rb file, or in an initializer.
65
+
66
+ require 'entity_storage'
67
+ DEFAULT_KEYS = { "testkey" => DateTime.parse("1-1-900"), "also test" => 2, "long ass key that I probably wouldn't use" => false }
68
+ EntityStore = EntityStorage::Storage.new(DEFAULT_KEYS)
69
+
70
+ On initialization, if the table "entity_storage" doesn't exist, it will be created. If you already have a table with that name, it must be in the correct format (previously created by this gem.)
71
+
72
+ You can pass a hash full of default key/value pairs. If the application accesses one of the keys, and it doesn't already exist, it will initiliaze that key with the default value. Good for getting your app to an intial starting state.
73
+
74
+
75
+ == LICENSE:
76
+
77
+ (The MIT License)
78
+
79
+ Copyright (c) 2009 Joshua Siler
80
+
81
+ Permission is hereby granted, free of charge, to any person obtaining
82
+ a copy of this software and associated documentation files (the
83
+ 'Software'), to deal in the Software without restriction, including
84
+ without limitation the rights to use, copy, modify, merge, publish,
85
+ distribute, sublicense, and/or sell copies of the Software, and to
86
+ permit persons to whom the Software is furnished to do so, subject to
87
+ the following conditions:
88
+
89
+ The above copyright notice and this permission notice shall be
90
+ included in all copies or substantial portions of the Software.
91
+
92
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
93
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
94
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
95
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
96
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
97
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
98
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/entity_storage'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'entity_storage' do
14
+ self.developer 'Joshua Siler', 'joshua.siler@gmail.com'
15
+ # self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ self.extra_deps = [['activerecord','>= 2.2.2']]
18
+ end
19
+
20
+ require 'newgem/tasks'
21
+ Dir['tasks/**/*.rake'].each { |t| load t }
22
+
23
+ # TODO - want other tests/tasks run by default? Add them to the list
24
+ # remove_task :default
25
+ # task :default => [:spec, :features]
@@ -0,0 +1,8 @@
1
+
2
+ adapter: mysql
3
+ database: careers
4
+ username: root
5
+ password: bnjstyle
6
+ host: localhost
7
+
8
+
@@ -0,0 +1,147 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'activerecord'
6
+
7
+ module EntityStorage
8
+ VERSION = '1.0.4'
9
+
10
+ class Storage
11
+ attr_accessor :defaults
12
+
13
+ # Checks for the existence of the necessary Entities table... if not here, creates it.
14
+ def initialize(defaults={})
15
+ unless ActiveRecord::Base.connection.table_exists?('entity_storage')
16
+ AddEntitiesTable.up
17
+ end
18
+
19
+ self.defaults = defaults
20
+ end
21
+
22
+ # Read a value.
23
+ def [](index)
24
+ Entity.get_value(index,@defaults[index.to_s])
25
+ end
26
+
27
+ # Write a value.
28
+ def []=(index,value)
29
+ Entity.set_value(index,value)
30
+ end
31
+
32
+ # Deletes a key and associated data from store.
33
+ def delete(index)
34
+ Entity.remove_item(index)
35
+ end
36
+
37
+ # Returns the default value of a key contained in DEFAULT_KEYS global constant.
38
+ # Does not change the stored value. Use default! to reset the value.
39
+ def default(index)
40
+ @defaults[index.to_s]
41
+ end
42
+
43
+ # Resets the default value of a key contained in DEFAULT_KEYS global constant and returns the value.
44
+ def default!(index)
45
+ Entity.reset_value(index,@defaults[index.to_s])
46
+ end
47
+
48
+ # Allows EntityStorage[:whatever] to be accessed via EntityStorage.whatever.
49
+ def method_missing(*args)
50
+ if args.length == 1
51
+ self[args[0]]
52
+ elsif args.length == 2 and args[0].to_s =~ /^(.*)=$/
53
+ self[$1.intern] = args[1]
54
+ else
55
+ super
56
+ end
57
+ end
58
+ end
59
+
60
+ # This migration is required for EntityStorage to work correctly
61
+ class AddEntitiesTable < ActiveRecord::Migration
62
+ def self.up
63
+ create_table "entity_storage", :force => true do |t|
64
+ t.string "key", :limit => 512, :null => false
65
+ t.text "value"
66
+ t.datetime "created_at"
67
+ t.datetime "updated_at"
68
+ end
69
+
70
+ add_index "entity_storage", ["created_at"], :name => "created_at"
71
+ add_index "entity_storage", ["key"], :name => "key"
72
+ add_index "entity_storage", ["updated_at"], :name => "updated_at"
73
+ end
74
+
75
+ def self.down
76
+ drop_table :entities
77
+ end
78
+ end
79
+
80
+
81
+ class Entity < ActiveRecord::Base
82
+ set_table_name "entity_storage"
83
+
84
+ # Entities should be used via class methods and not instantiated directly.
85
+ private_class_method :new
86
+
87
+ # Gets value based on specific key.
88
+ # If not found, will initialize according to defaults set in DEFAULT_KEYS global constant.
89
+ def self.get_value(search_key,default_value)
90
+ e = Entity.find_by_key(search_key.to_s)
91
+ if e.nil? || e.value.nil?
92
+ e = initialize_value(search_key,default_value)
93
+ end
94
+ e.value rescue nil
95
+ end
96
+
97
+ # Sets value for a specific key. If key doesn't exist, creates with value.
98
+ def self.set_value(search_key, new_value)
99
+ e = Entity.find_by_key(search_key.to_s)
100
+ if e.nil?
101
+ e = new
102
+ end
103
+ e.key = search_key
104
+ e.value = new_value
105
+ e.save
106
+ end
107
+
108
+ # Resets a key contained in DEFAULT_KEYS global constant to it's default value
109
+ def self.reset_value(search_key,default_value)
110
+ Entity.remove_item(search_key)
111
+ initialize_value(search_key,default_value).value
112
+ end
113
+
114
+ # Deletes a record from key store.
115
+ def self.remove_item(search_key)
116
+ e = Entity.find_by_key(search_key.to_s)
117
+ e.destroy rescue 0
118
+ end
119
+
120
+ def value=(data)
121
+ write_attribute(:value,Marshal.dump(data))
122
+ end
123
+
124
+ def value
125
+ Marshal.load(read_attribute(:value))
126
+ end
127
+
128
+ def key=(newkey)
129
+ write_attribute(:key,newkey.to_s)
130
+ end
131
+
132
+ private
133
+
134
+ # Checks provided key against internal defaults list and initializes to default value.
135
+ # Defaults defined in DEFAULT_KEYS global constant.
136
+ def self.initialize_value(search_key,default_value)
137
+ en = new
138
+ en.key = search_key
139
+ en.value = default_value
140
+ en.save
141
+ en
142
+ end
143
+
144
+ end
145
+
146
+ end
147
+
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/entity_storage.rb'}"
9
+ puts "Loading entity_storage gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,107 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ ActiveRecord::Base.establish_connection(YAML::load(File.read(File.dirname(__FILE__) + '/../config/database.yml')))
3
+
4
+ class TestEntityStorage < Test::Unit::TestCase
5
+ DEFAULT_KEYS = { "test" => DateTime.parse("1-1-900"), "also test" => 2, "long ass key that I probably wouldn't use" => false,
6
+ # last time the hiring manager notifications have been run
7
+ "new_applicant_last_run" => DateTime.parse("1-1-1900"),
8
+ # last time the new job notifications have been run
9
+ "new_jobs_last_run" => DateTime.parse("1-1-1900"),
10
+ "email_test_address" => "joshuas@bnj.com" }
11
+
12
+ attr_accessor :EntityStore
13
+ EntityStore = EntityStorage::Storage.new(DEFAULT_KEYS)
14
+
15
+ def setup
16
+ ActiveRecord::Base.connection.execute("delete from entity_storage")
17
+ end
18
+
19
+ def test_instantiation
20
+ # test nasty weird bug with underscore characters
21
+ entityStore2 = EntityStorage::Storage.new(DEFAULT_KEYS)
22
+ assert_equal entityStore2.email_test_address, 'joshuas@bnj.com'
23
+ end
24
+
25
+ # tests value setting and getting functionality, along with default creation
26
+ def test_defaultkeys
27
+ DEFAULT_KEYS.each { |key,value|
28
+ EntityStore.delete(key)
29
+
30
+ # set key when it doesn't exist, should go to default
31
+ e = EntityStore[key]
32
+ assert_equal e, value
33
+
34
+ EntityStore.delete(key)
35
+
36
+ # set and try method missing access
37
+ if key[/\w/] == key
38
+ eval("e = EntityStore."+key)
39
+ assert_equal e, value
40
+ end
41
+
42
+ # change it to something else
43
+ EntityStore[key] = Time.now.to_s
44
+ e = EntityStore[key]
45
+ assert_not_equal e, value
46
+
47
+ # find out it's default
48
+ e = EntityStore.default(key)
49
+ assert_equal e, value
50
+
51
+ e = EntityStore.defaults[key]
52
+ assert_equal e, value
53
+
54
+ # set it back to default
55
+ EntityStore.default!(key)
56
+ e = EntityStore[key]
57
+ assert_equal e, value
58
+
59
+ # set it to something else using method missing
60
+ if key[/\w/] == key
61
+ eval("e = EntityStore."+key +" = Time.now.to_s")
62
+ assert_equal e, value
63
+ end
64
+ }
65
+ end
66
+
67
+ # tests setting values when no DEFAULT_KEY exists
68
+ def test_settingvalues
69
+ EntityStore.delete('test')
70
+
71
+ # key that can't be accessed via method missing
72
+ # set value and check it
73
+ EntityStore['big long key that cannot be accessed via method missing'] = 'what up'
74
+ e = EntityStore['big long key that cannot be accessed via method missing']
75
+ assert_equal e, 'what up'
76
+
77
+ # set another value and check it
78
+ EntityStore['big long key that cannot be accessed via method missing'] = 'another value'
79
+ e = EntityStore['big long key that cannot be accessed via method missing']
80
+ assert_equal e, 'another value'
81
+
82
+ EntityStore['test'] = 'what up'
83
+ e = EntityStore['test']
84
+ assert_equal e, 'what up'
85
+
86
+ EntityStore['test'] = 'another value'
87
+ e = EntityStore['test']
88
+ assert_equal e, 'another value'
89
+
90
+ # try the method missing version of the tests
91
+ e = EntityStore.test = 'what up'
92
+ e = EntityStore.test
93
+ assert_equal e, 'what up'
94
+
95
+ e = EntityStore.test = 'another value'
96
+ e = EntityStore.test
97
+ assert_equal e, 'another value'
98
+
99
+ end
100
+
101
+ def test_getunknownvalue
102
+ EntityStore.delete('does not exist')
103
+ e = EntityStore['does not exist']
104
+ assert_nil e
105
+ end
106
+
107
+ end
@@ -0,0 +1,6 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require 'rubygems'
4
+ require 'active_record'
5
+ require 'active_support'
6
+ require File.dirname(__FILE__) + '/../lib/entity_storage'
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: entity_storage
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Siler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-23 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.2.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.3
34
+ version:
35
+ description: |-
36
+ An easy to use Key/Value store for any Ruby on Rails project. Like Memcache, only persistent. Stores config values &amp; application wide state in the database in order to survive server restarts.
37
+
38
+ Designed to allow you to add persistent value storage to any Rails project in about 5 minutes.
39
+
40
+ Additionally, allows users to set a list of default keys that auto-initializes baseline key/value pairs in the database for easy initialization.
41
+ email:
42
+ - joshua.siler@gmail.com
43
+ executables: []
44
+
45
+ extensions: []
46
+
47
+ extra_rdoc_files:
48
+ - History.txt
49
+ - Manifest.txt
50
+ files:
51
+ - History.txt
52
+ - Manifest.txt
53
+ - README.rdoc
54
+ - Rakefile
55
+ - lib/entity_storage.rb
56
+ - script/console
57
+ - script/destroy
58
+ - script/generate
59
+ - config/database.yml
60
+ - test/test_entity_storage.rb
61
+ - test/test_helper.rb
62
+ has_rdoc: true
63
+ homepage: http://github.com/eatenbyagrue/entity_storage
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --main
69
+ - README.rdoc
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ requirements: []
85
+
86
+ rubyforge_project: entity_storage
87
+ rubygems_version: 1.3.5
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: An easy to use Key/Value store for any Ruby on Rails project
91
+ test_files:
92
+ - test/test_entity_storage.rb
93
+ - test/test_helper.rb