enum-rb 0.1.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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in renum.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 jake
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.
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Enum-rb
2
+
3
+ Enum-rb tries to be a flexible way to store arbitrary data that your
4
+ application needs to function but doesn't really fit anywhere and
5
+ has no defined structure.
6
+
7
+ The idea is to use it to store things like `key => value` mappings for
8
+ your `select` boxes, or any mappings for that matter. Anytime you need
9
+ to just have some bit of formless data, Enum should be able to hold it.
10
+
11
+ Have more of these mappings than it would be smart to store in-memory?
12
+ No worries, Enum-rb comes with a `RedisStore` that will store your
13
+ enums in Redis.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'enum-rb'
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install enum-rb
28
+
29
+ ## Usage
30
+
31
+ You can use Enum-rb in two ways. If having a single store is all you need,
32
+ you can set
33
+
34
+ Enum.default_backend
35
+
36
+ # to either a populated `MemoryStore`, which you can get with
37
+
38
+ Enum.default_backend = Enum::MemoryStore.new({your: {'enum' => 'data'}})
39
+
40
+ # or a configured `RedisStore`, which you can get with
41
+
42
+ Enum.default_backend = Enum::RedisStore.new(configured_redis_namespace)
43
+
44
+ # and then use
45
+
46
+ Enum[:your] # => {'enum' => 'data'}
47
+ Enum.fetch(:your) # => {'enum' => 'data'}
48
+
49
+ # unknown Enums cause exceptions
50
+
51
+ Enum[:nope] # => ArgumentError
52
+
53
+ Or you can alternatively have multiple enum stores by creating an instance
54
+ of `Enum` and passing it the store to use.
55
+
56
+ @enum = Enum.new(Enum::MemoryStore.new({a: 'small', amount: ['of', 'enums']}))
57
+
58
+ @enum[:a] # => 'small'
59
+ @enum.fetch(:amount) # => ['of', 'enums']
60
+
61
+ # unknown Enums cause exceptions
62
+
63
+ @enum.feth(:nope) # => ArgumentError
64
+
65
+ If you are using the `RedisStore`, you can use `RedisStore#load_hash` or
66
+ `RedisStore#load_yaml` to load the store. In redis, you can't use symbols
67
+ as names, but the `RedisStore` will handle storing nested hashes or arrays.
68
+
69
+ For instance you could call:
70
+
71
+ Enum::RedisStore.new(configured_redis).load_hash(
72
+ {
73
+ 'select_options' => [
74
+ {'name' => 'howdy', 'value' => 'hello'},
75
+ {'name' => 'later', 'value' => 'bye'}
76
+ ],
77
+ 'other_stuff' => {
78
+ ['somehow', 'this', 'seems', 'important']
79
+ },
80
+
81
+ '2 + 2' => '5'
82
+ }
83
+ )
84
+
85
+ and the `RedisStore` will get all that saved so that
86
+
87
+ Enum['select_options']
88
+ # => [{'name' => 'howdy', 'value' => 'hello'}, {...}]
89
+
90
+ ## Contributing
91
+
92
+ 1. Fork it
93
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
94
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
95
+ 4. Push to the branch (`git push origin my-new-feature`)
96
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << File.join(File.dirname(__FILE__), 'lib')
6
+ t.test_files = FileList['test/**/test*.rb']
7
+ t.verbose = true
8
+ end
data/enum-rb.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'enum/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "enum-rb"
8
+ gem.version = Enum::VERSION
9
+ gem.authors = ["jake"]
10
+ gem.email = ["pablo_honey@me.com"]
11
+ gem.description = %q{Ruby Enum storage.}
12
+ gem.summary = %q{Store some things vaguely similar to enums. In Ruby}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'redis-namespace'
21
+ end
@@ -0,0 +1,23 @@
1
+
2
+ class Enum
3
+ class MemoryStore
4
+
5
+ def initialize(store = nil)
6
+ @store = store || Hash.new
7
+ end
8
+
9
+ def get_value(key)
10
+ @store.fetch(key) rescue fail_fetch(key)
11
+ end
12
+
13
+ def set_value(name, value)
14
+ @store[name] = value
15
+ end
16
+
17
+ private
18
+
19
+ def fail_fetch(key)
20
+ raise ArgumentError.new("#{key} is not defined.")
21
+ end
22
+ end # => MemoryStore
23
+ end # => Enum
@@ -0,0 +1,138 @@
1
+ require 'redis/namespace'
2
+
3
+ class Enum
4
+ class RedisStore
5
+
6
+ attr_reader :connection
7
+
8
+ def initialize(redis = nil)
9
+ @connection = redis || Redis::Namespace.new(:enum, redis: Redis.new)
10
+ end
11
+
12
+ def get_value(key)
13
+ Getter.get_value(connection, key)
14
+ end
15
+
16
+ def set_value(key, value)
17
+ Setter.set_value(connection, key, value)
18
+ end
19
+
20
+ def load_yaml(file)
21
+ require 'yaml'
22
+ load_hash(YAML.load_file(file))
23
+ end
24
+
25
+ def load_hash(hash)
26
+ hash.each_pair do |key, value|
27
+ Setter.set_value(connection, key, value)
28
+ end
29
+ end
30
+
31
+ module Setter
32
+
33
+ module_function
34
+
35
+ def set_value(connection, key, value)
36
+ meth = :"set_#{value.class.to_s.downcase}_value"
37
+ raise_cannot_store_value_type(value) unless self.respond_to?(meth)
38
+
39
+ send(meth, connection, key, value)
40
+ end
41
+
42
+ def set_string_value(connection, key, value)
43
+ connection.set(key, value)
44
+ end
45
+
46
+ def set_array_value(connection, key, value)
47
+ connection.multi do
48
+ value.each_with_index do |v, index|
49
+ (connection.rpush(key, v) && next) if v.is_a?(String)
50
+ nested_value_key(key, index).tap do |nvkey|
51
+ set_value(connection, nvkey, v)
52
+ connection.rpush(key, "ENUM_KEY:#{nvkey}")
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def set_hash_value(connection, key, value)
59
+ connection.multi do
60
+ settable_hash = value.inject(Hash.new) do |out, pair|
61
+ if pair.last.is_a?(String) || pair.last.is_a?(Fixnum)
62
+ out[pair.first] = pair.last
63
+ else
64
+ nested_value_key(key, pair.first).tap do |nvkey|
65
+ set_value(connection, nvkey, pair.last)
66
+ out[pair.first] = "ENUM_KEY:#{nvkey}"
67
+ end
68
+ end
69
+ out
70
+ end
71
+
72
+ connection.hmset(key, *settable_hash.to_a.flatten)
73
+ end
74
+ end
75
+
76
+ def nested_value_key(base, addy)
77
+ "nested.value:#{base}.#{addy}"
78
+ end
79
+
80
+ def namespace_name(name)
81
+ return name if name[0..@name.length] == @name
82
+ "#@name.#{name}"
83
+ end
84
+
85
+ def raise_cannot_store_value_type(value)
86
+ raise "Enum cannot store values of class #{value.class}."
87
+ end
88
+
89
+ end # => Setter
90
+
91
+ module Getter
92
+
93
+ module_function
94
+
95
+ def get_value(connection, key)
96
+ raise_unknown_key(key) unless connection.exists(key)
97
+
98
+ meth = :"get_#{connection.type(key)}_value"
99
+
100
+ raise_unknown_value_type(meth, key) unless self.respond_to?(meth)
101
+
102
+ send(meth, connection, key)
103
+ end
104
+
105
+ def get_string_value(connection, key)
106
+ connection.get(key)
107
+ end
108
+
109
+ def get_list_value(connection, key)
110
+ connection.lrange(key, 0, -1).map do |value|
111
+ is_enum_key?(value) ? get_value(connection, value[9..-1]) : value
112
+ end
113
+ end
114
+
115
+ def get_hash_value(connection, key)
116
+ connection.hgetall(key).inject(Hash.new) do |out, pair|
117
+ k, v = pair
118
+ out[k] = is_enum_key?(v) ? get_value(connection, v[9..-1]) : v
119
+ out
120
+ end
121
+ end
122
+
123
+ def is_enum_key?(key)
124
+ !/ENUM_KEY:.*/.match(key.to_s).nil?
125
+ end
126
+
127
+ def raise_unknown_value_type(meth, key)
128
+ raise "#{key} has unknown value type: #{meth} is not defined."
129
+ end
130
+
131
+ def raise_unknown_key(key)
132
+ raise ArgumentError.new("Enum [ #{key} ] is not defined.")
133
+ end
134
+
135
+ end # => Getter
136
+
137
+ end
138
+ end
@@ -0,0 +1,3 @@
1
+ module Enum
2
+ VERSION = "0.1.1"
3
+ end
data/lib/enum.rb ADDED
@@ -0,0 +1,35 @@
1
+
2
+ class Enum
3
+
4
+ class << self
5
+
6
+ def default_backend
7
+ @default_backend ||= begin
8
+ require 'enum/store/memory_store'
9
+ Enum::MemoryStore.new
10
+ end
11
+ end
12
+
13
+ def default_backend=(arg)
14
+ @default_backend = arg
15
+ end
16
+
17
+ def [](arg)
18
+ default_backend.get_value(arg)
19
+ end
20
+ alias fetch []
21
+
22
+ end
23
+
24
+ attr_reader :name, :backend
25
+
26
+ def initialize(backend = nil)
27
+ @backend = backend || Enum.default_backend
28
+ end
29
+
30
+ def fetch(name)
31
+ backend.get_value(name)
32
+ end
33
+ alias [] fetch
34
+
35
+ end
@@ -0,0 +1,29 @@
1
+ require 'test/unit'
2
+ require 'enum'
3
+ require 'enum/store/memory_store'
4
+
5
+ class TestEnumStoreMemoryStore < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @store = Enum::MemoryStore.new({'test' => 'foobar'})
9
+ end
10
+
11
+ def test_set_value
12
+ @store.set_value('test', 'foobar')
13
+
14
+ assert_equal 'foobar', @store.instance_variable_get(:@store)['test']
15
+ end
16
+
17
+ def test_get_value
18
+ @store.instance_variable_set(:@store, {'test' => 'foobar'})
19
+
20
+ assert_equal 'foobar', @store.get_value('test')
21
+ end
22
+
23
+ def test_raises_on_undefined_value
24
+ assert_raises ArgumentError do
25
+ @store.get_value(:test)
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,121 @@
1
+ require 'test/unit'
2
+ require 'enum'
3
+ require 'enum/store/redis_store'
4
+
5
+
6
+ class TestEnumStoreRedisStore < Test::Unit::TestCase
7
+ def setup
8
+ @store = Enum::RedisStore.new(
9
+ Redis::Namespace.new(:test_enums, redis: Redis.new, db: 11))
10
+ @store.connection.flushdb
11
+ end
12
+
13
+ def teardown
14
+ @store.connection.flushdb
15
+ end
16
+
17
+ def test_load_hash
18
+ @store.load_hash({'foo' => 'bar', 'baz' => 'bat'})
19
+
20
+ assert_equal 'bar', @store.get_value('foo')
21
+ assert_equal 'bat', @store.get_value('baz')
22
+ end
23
+
24
+ def test_sets_string_values
25
+ @store.set_value('test', 'hat')
26
+
27
+ assert_equal 'hat', @store.connection.get('test')
28
+ end
29
+
30
+ def test_gets_string_values
31
+ @store.connection.set('test', 'hat')
32
+
33
+ assert_equal 'hat', @store.get_value('test')
34
+ end
35
+
36
+ def test_sets_list_values
37
+ arr = %w{1 2 3}
38
+ @store.set_value('test', arr)
39
+
40
+ assert_equal 'list', @store.connection.type('test')
41
+ assert_equal arr, @store.connection.lrange('test', 0, -1)
42
+ end
43
+
44
+ def test_sets_list_values_with_complex_entries
45
+ arr = [['1','2'], {'3' => '4'}, '5']
46
+ @store.set_value('test', arr)
47
+
48
+ assert_equal 'list', @store.connection.type('test')
49
+ assert_equal(
50
+ ['ENUM_KEY:nested.value:test.0', 'ENUM_KEY:nested.value:test.1', '5'],
51
+ @store.connection.lrange('test', 0, -1))
52
+
53
+ assert_equal 'list', @store.connection.type('nested.value:test.0')
54
+ assert_equal ['1', '2'], @store.connection.lrange('nested.value:test.0', 0, -1)
55
+
56
+ assert_equal 'hash', @store.connection.type('nested.value:test.1')
57
+ assert_equal({'3' => '4'}, @store.connection.hgetall('nested.value:test.1'))
58
+ end
59
+
60
+ def test_gets_list_values
61
+ %w{1 2 3}.each {|i| @store.connection.rpush('test', i)}
62
+
63
+ assert_equal %w{1 2 3}, @store.get_value('test')
64
+ end
65
+
66
+ def test_gets_list_values_with_complex_types
67
+ %w{1 2}.each {|i| @store.connection.rpush('nested.value:test.0', i)}
68
+ @store.connection.hmset('nested.value:test.1', '3', '4')
69
+ %w{ENUM_KEY:nested.value:test.0 ENUM_KEY:nested.value:test.1 5}.each do |s|
70
+ @store.connection.rpush('test', s)
71
+ end
72
+ arr = [['1', '2'], {'3' => '4'}, '5']
73
+
74
+ assert_equal arr, @store.get_value('test')
75
+ end
76
+
77
+ def test_sets_hash_values
78
+ hsh = {'1' => '2', '3' => '4'}
79
+ @store.set_value('test', hsh)
80
+
81
+ assert_equal 'hash', @store.connection.type('test')
82
+ assert_equal hsh, @store.connection.hgetall('test')
83
+ end
84
+
85
+ def test_sets_hash_values_with_complex_types
86
+ hsh = {'1' => ['2', '3'], '4' => {'5' => '6'}, '7' => '8'}
87
+ @store.set_value('test', hsh)
88
+
89
+ assert_equal(
90
+ {'1' => 'ENUM_KEY:nested.value:test.1',
91
+ '4' => 'ENUM_KEY:nested.value:test.4', '7' => '8'},
92
+ @store.connection.hgetall('test'))
93
+
94
+ assert_equal ['2', '3'],
95
+ @store.connection.lrange('nested.value:test.1', 0, -1)
96
+ assert_equal({'5' => '6'},
97
+ @store.connection.hgetall('nested.value:test.4'))
98
+ end
99
+
100
+ def test_gets_hash_values
101
+ @store.connection.hmset('test', '1', '2', '3', '4')
102
+
103
+ assert_equal({'1' => '2', '3' => '4'}, @store.get_value('test'))
104
+ end
105
+
106
+ def test_gets_hash_values_with_complex_types
107
+ %w{2 3}.each {|i| @store.connection.rpush('nested.value:test.1', i)}
108
+ @store.connection.hmset('nested.value:test.4', '5', '6')
109
+ @store.connection.hmset('test', 1, 'ENUM_KEY:nested.value:test.1', 4,
110
+ 'ENUM_KEY:nested.value:test.4', 7, 8)
111
+
112
+ hsh = {'1' => ['2', '3'], '4' => {'5' => '6'}, '7' => '8'}
113
+ assert_equal hsh, @store.get_value('test')
114
+ end
115
+
116
+ def test_raises_on_unset_values
117
+ assert_raises ArgumentError do
118
+ @store.get_value('unset.test')
119
+ end
120
+ end
121
+ end
data/test/test_enum.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'test/unit'
2
+ require 'enum'
3
+
4
+ class TestEnum < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @enum = Enum.new()
8
+ end
9
+
10
+ def test_class_fetch
11
+ Enum.default_backend = Enum::MemoryStore.new({test: 'ahoy!'})
12
+
13
+ assert_equal 'ahoy!', Enum.fetch(:test)
14
+ assert_equal 'ahoy!', Enum[:test]
15
+ end
16
+
17
+ def test_initializes_with_defaults
18
+ assert_equal Enum.default_backend, @enum.instance_variable_get(:@backend)
19
+ end
20
+
21
+ def test_value_calls_backend
22
+ @enum.instance_variable_get(:@backend).set_value(:test, 'foobar')
23
+
24
+ assert_equal 'foobar', @enum.fetch(:test)
25
+ assert_equal 'foobar', @enum[:test]
26
+ end
27
+
28
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: enum-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - jake
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: redis-namespace
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
+ description: Ruby Enum storage.
31
+ email:
32
+ - pablo_honey@me.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
42
+ - enum-rb.gemspec
43
+ - lib/enum.rb
44
+ - lib/enum/store/memory_store.rb
45
+ - lib/enum/store/redis_store.rb
46
+ - lib/enum/version.rb
47
+ - test/enum/store/test_memory_store.rb
48
+ - test/enum/store/test_redis_store.rb
49
+ - test/test_enum.rb
50
+ homepage: ''
51
+ licenses: []
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.23
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Store some things vaguely similar to enums. In Ruby
74
+ test_files:
75
+ - test/enum/store/test_memory_store.rb
76
+ - test/enum/store/test_redis_store.rb
77
+ - test/test_enum.rb
78
+ has_rdoc: