horcrux 0.0.1 → 0.0.2

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/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :dev do
6
+ gem 'rake'
7
+ gem 'msgpack'
8
+ end
9
+
data/horcrux.gemspec CHANGED
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
12
12
  ## If your rubyforge_project name is different, then edit it and comment out
13
13
  ## the sub! line in the Rakefile
14
14
  s.name = 'horcrux'
15
- s.version = '0.0.1'
16
- s.date = '2012-01-01'
15
+ s.version = '0.0.2'
16
+ s.date = '2012-01-03'
17
17
  s.rubyforge_project = 'horcrux'
18
18
 
19
19
  ## Make sure your summary is short. The description may be as long
@@ -40,12 +40,19 @@ Gem::Specification.new do |s|
40
40
  ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
41
41
  # = MANIFEST =
42
42
  s.files = %w[
43
+ Gemfile
43
44
  LICENSE.md
44
45
  README.md
45
46
  Rakefile
46
47
  horcrux.gemspec
47
48
  lib/horcrux.rb
49
+ lib/horcrux/entity.rb
50
+ lib/horcrux/serializers/gzip_serializer.rb
51
+ lib/horcrux/serializers/message_pack_serializer.rb
52
+ test/entity_test.rb
53
+ test/helper.rb
48
54
  test/memory_test.rb
55
+ test/serializer_test.rb
49
56
  ]
50
57
  # = MANIFEST =
51
58
 
@@ -0,0 +1,179 @@
1
+ require File.expand_path('../../horcrux', __FILE__)
2
+ require 'set'
3
+ require 'time'
4
+
5
+ module Horcrux
6
+ module Entity
7
+ def self.included(base)
8
+ class << base
9
+ attr_accessor :attributes, :writable_attributes, :default_attribute
10
+ end
11
+
12
+ base.attributes = Set.new
13
+ base.writable_attributes = Set.new
14
+ base.extend ClassMethods
15
+ end
16
+
17
+ module ClassMethods
18
+ # Public
19
+ def attr(type, *keys)
20
+ method = "build_#{type}_attr"
21
+ if respond_to?(method)
22
+ send method, keys, false
23
+ else
24
+ raise TypeError, "Type #{type.inspect} not recognized."
25
+ end
26
+ end
27
+
28
+ # Public
29
+ def readonly(type, *keys)
30
+ method = "build_#{type}_attr"
31
+ if respond_to?(method)
32
+ send method, keys, true
33
+ else
34
+ raise TypeError, "Type #{type.inspect} not recognized."
35
+ end
36
+ end
37
+
38
+ # Public
39
+ def from(hash = {})
40
+ hash.respond_to?(@default_attribute) ? hash : new(hash)
41
+ end
42
+
43
+ def build_string_attr(keys, is_readonly)
44
+ attr_reader *keys
45
+
46
+ build_attrs(keys, is_readonly) do |key_s|
47
+ ivar_key = "@#{key_s}"
48
+ define_method "#{key_s}=" do |value|
49
+ instance_variable_set ivar_key, value
50
+ end
51
+ end
52
+ end
53
+
54
+ def build_bool_attr(keys, is_readonly)
55
+ attr_reader *keys
56
+
57
+ build_attrs(keys, is_readonly) do |key_s|
58
+ ivar_key = "@#{key_s}"
59
+
60
+ define_method "#{key_s}?" do
61
+ !!instance_variable_get(ivar_key)
62
+ end
63
+
64
+ define_method "#{key_s}=" do |value|
65
+ instance_variable_set ivar_key, case value
66
+ when Fixnum then value > 0
67
+ when /t|f/i then value =~ /t/i ? true : false
68
+ else !!value
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def build_array_attr(keys, is_readonly)
75
+ build_class_attr(Array, keys, is_readonly)
76
+ end
77
+
78
+ def build_hash_attr(keys, is_readonly)
79
+ build_class_attr(Hash, keys, is_readonly)
80
+ end
81
+
82
+ def build_time_attr(keys, is_readonly)
83
+ attr_reader *keys
84
+
85
+ build_attrs(keys, is_readonly) do |key_s|
86
+ ivar_key = "@#{key_s}"
87
+ define_method "#{key_s}=" do |value|
88
+ instance_variable_set ivar_key,
89
+ value.respond_to?(:utc) ?
90
+ value :
91
+ Time.at(value.to_i).utc
92
+ end
93
+ end
94
+ end
95
+
96
+ def build_class_attr(klass, keys, is_readonly)
97
+ build_attrs(keys, is_readonly) do |key_s|
98
+ ivar_key = "@#{key_s}"
99
+ define_method key_s do
100
+ instance_variable_get(ivar_key) ||
101
+ instance_variable_set(ivar_key, klass.new)
102
+ end
103
+
104
+ define_method "#{key_s}=" do |value|
105
+ unless value.is_a?(klass)
106
+ raise TypeError, "#{key_s} should be a #{klass}: #{value.inspect}"
107
+ end
108
+ instance_variable_set(ivar_key, value)
109
+ end
110
+ end
111
+ end
112
+
113
+ def build_attrs(keys, is_readonly)
114
+ keys.each do |key|
115
+ key_s = key.to_s
116
+ @default_attribute ||= key_s
117
+ @attributes << key_s
118
+ @writable_attributes << key_s unless is_readonly
119
+ yield key_s
120
+ end
121
+ end
122
+ end
123
+
124
+ # Public
125
+ def initialize(hash = {})
126
+ update_attrs(hash, all = true)
127
+ end
128
+
129
+ # Public: Updates multiple attributes.
130
+ #
131
+ # hash - A Hash containing valid attributes.
132
+ #
133
+ # Returns this Entity.
134
+ def update_attrs(hash, all = false)
135
+ attr = all ? self.class.attributes : self.class.writable_attributes
136
+ hash.each do |key, value|
137
+ key_s = key.to_s
138
+
139
+ if !attr.include?(key_s)
140
+ raise ArgumentError, "Invalid property: #{key.inspect}"
141
+ end
142
+
143
+ send "#{key_s}=", value
144
+ end
145
+
146
+ self
147
+ end
148
+
149
+ # Public
150
+ def each_attr(writable_only = false)
151
+ attr_method = writable_only ? :writable_attributes : :attributes
152
+ self.class.send(attr_method).each do |key|
153
+ if value = send(key)
154
+ yield key, value
155
+ end
156
+ end
157
+ self
158
+ end
159
+
160
+ # Public
161
+ def ==(other)
162
+ default_attr = self.class.default_attribute
163
+ other.class == self.class && other.send(default_attr) == send(default_attr)
164
+ end
165
+
166
+ # Public: Converts the valid attributes into a Hash of Symbol key and Object
167
+ # value.
168
+ #
169
+ # Returns a Hash.
170
+ def to_hash(writable_only = false)
171
+ hash = {}
172
+ each_attr(writable_only) do |key, value|
173
+ hash[key.to_sym] = value
174
+ end
175
+ hash
176
+ end
177
+ end
178
+ end
179
+
@@ -0,0 +1,29 @@
1
+ require File.expand_path('../../../horcrux', __FILE__)
2
+ require 'zlib'
3
+ require 'stringio'
4
+
5
+ module Horcrux
6
+ class GzipSerializer
7
+ def initialize(serializer)
8
+ @serializer = serializer
9
+ end
10
+
11
+ def dump(value)
12
+ s = StringIO.new
13
+ z = Zlib::GzipWriter.new(s)
14
+ z.write @serializer.dump(value)
15
+ s.string
16
+ ensure
17
+ z.close if z
18
+ end
19
+
20
+ def load(value)
21
+ s = StringIO.new(value)
22
+ z = Zlib::GzipReader.new(s)
23
+ @serializer.load(z.read)
24
+ ensure
25
+ z.close if z
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../../../horcrux', __FILE__)
2
+ require 'msgpack'
3
+
4
+ module Horcrux
5
+ module MessagePackSerializer
6
+ extend self
7
+
8
+ def dump(value)
9
+ value.to_msgpack
10
+ end
11
+
12
+ def load(str)
13
+ MessagePack.unpack(str)
14
+ end
15
+ end
16
+ end
17
+
18
+ if Time.now.respond_to?(:to_msgpack)
19
+ raise LoadError, "Time#to_msgpack should not exist"
20
+ else
21
+ class Time
22
+ def to_msgpack(*args)
23
+ to_i.to_msgpack(*args)
24
+ end
25
+ end
26
+ end
27
+
data/lib/horcrux.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # See the README.md
2
2
  module Horcrux
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
 
5
5
  # Implements the optional methods of a Horcrux adapter.
6
6
  module Methods
@@ -0,0 +1,138 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ module Horcrux
4
+ class EntityTest < Test::Unit::TestCase
5
+ class Entity
6
+ include Horcrux::Entity
7
+
8
+ attr :string, :name
9
+ attr :bool, :admin
10
+ attr :array, :roles
11
+ attr :time, :updated_at
12
+
13
+ readonly :string, :type
14
+ readonly :hash, :codes
15
+ readonly :time, :created_at
16
+
17
+ def initialize(hash)
18
+ super(hash)
19
+ @created_at ||= Time.now.utc
20
+ end
21
+
22
+ def type
23
+ @type ||= 'default'
24
+ end
25
+ end
26
+
27
+ def test_accesses_attributes
28
+ ent = entity
29
+ assert_equal 'bob', ent.name
30
+ assert_equal 'person', ent.type
31
+ assert_equal true, ent.admin
32
+ assert ent.admin?
33
+
34
+ ent.name = 'bobby'
35
+ assert_equal 'bobby', ent.name
36
+
37
+ ent.admin = 0
38
+ assert !ent.admin?
39
+
40
+ ent.admin = 'true'
41
+ assert ent.admin?
42
+
43
+ ent.admin = false
44
+ assert !ent.admin?
45
+
46
+ ent.roles = %w(c)
47
+ assert_equal %w(c), ent.roles
48
+
49
+ assert_equal 2000, ent.created_at.year
50
+ assert_equal 2012, ent.updated_at.year
51
+
52
+ ent.updated_at = Time.utc 2010
53
+ assert_equal 2010, ent.updated_at.year
54
+
55
+ assert_raises ArgumentError do
56
+ ent.update_attrs :type => 'troll'
57
+ end
58
+
59
+ assert_raises ArgumentError do
60
+ ent.update_attrs :codes => 'troll'
61
+ end
62
+
63
+ assert_raises ArgumentError do
64
+ ent.update_attrs :created_at => Time.now
65
+ end
66
+
67
+ assert_raises TypeError do
68
+ ent.roles = 1
69
+ end
70
+ end
71
+
72
+ def test_initializes_with_default_value
73
+ ent = entity(:created_at => nil)
74
+ assert_equal Time.now.utc.year, ent.created_at.year
75
+ end
76
+
77
+ def test_dumps_entity_to_hash
78
+ hash = entity.to_hash
79
+ assert_equal 'bob', hash[:name]
80
+ assert_equal 'person', hash[:type]
81
+ assert_equal true, hash[:admin]
82
+ assert_equal %w(a b), hash[:roles]
83
+ assert_equal({"a" => 1, "b" => 2}, hash[:codes])
84
+ assert_equal 2000, hash[:created_at].year
85
+ assert_equal 2012, hash[:updated_at].year
86
+ end
87
+
88
+ def test_tracks_attributes
89
+ assert Entity.attributes.include?('name')
90
+ assert Entity.attributes.include?('admin')
91
+ assert Entity.attributes.include?('type')
92
+ assert Entity.attributes.include?('roles')
93
+ assert Entity.attributes.include?('codes')
94
+ assert Entity.attributes.include?('created_at')
95
+ assert Entity.attributes.include?('updated_at')
96
+ end
97
+
98
+ def test_tracks_writable_attributes
99
+ assert Entity.writable_attributes.include?('name')
100
+ assert Entity.writable_attributes.include?('admin')
101
+ assert Entity.writable_attributes.include?('roles')
102
+ assert Entity.writable_attributes.include?('updated_at')
103
+ assert !Entity.writable_attributes.include?('codes')
104
+ assert !Entity.writable_attributes.include?('type')
105
+ assert !Entity.writable_attributes.include?('created_at')
106
+ end
107
+
108
+ def test_tracks_default_attribute
109
+ assert_equal 'name', Entity.default_attribute
110
+ end
111
+
112
+ def test_convert_hash_to_entity
113
+ data = entity_data :name => "fred"
114
+ ent = entity :name => 'fred'
115
+ assert_equal ent, Entity.from(data)
116
+ assert_equal ent, Entity.from(ent)
117
+ end
118
+
119
+ def entity(options = {})
120
+ Entity.new(entity_data(options))
121
+ end
122
+
123
+ def entity_data(options = {})
124
+ hash = {:name => 'bob', :type => 'person', :admin => 't',
125
+ :created_at => Time.utc(2000), :updated_at => 1325619163,
126
+ :roles => %w(a b), :codes => {'a' => 1, 'b' => 2}}
127
+ options.each do |key, value|
128
+ if value.nil?
129
+ hash.delete key
130
+ else
131
+ hash[key] = value
132
+ end
133
+ end
134
+ hash
135
+ end
136
+ end
137
+ end
138
+
data/test/helper.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'test/unit'
2
+ require File.expand_path('../../lib/horcrux', __FILE__)
3
+ require File.expand_path('../../lib/horcrux/serializers/gzip_serializer', __FILE__)
4
+ require File.expand_path('../../lib/horcrux/entity', __FILE__)
5
+
data/test/memory_test.rb CHANGED
@@ -1,5 +1,4 @@
1
- require 'test/unit'
2
- require File.expand_path('../../lib/horcrux', __FILE__)
1
+ require File.expand_path('../helper', __FILE__)
3
2
 
4
3
  module Horcrux
5
4
  class MemoryTest < Test::Unit::TestCase
@@ -62,3 +61,4 @@ module Horcrux
62
61
  end
63
62
  end
64
63
  end
64
+
@@ -0,0 +1,40 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ module Horcrux
4
+ class SerializerTest < Test::Unit::TestCase
5
+ def test_null_serializer
6
+ assert_equal 1, NullSerializer.dump(1)
7
+ assert_equal 1, NullSerializer.load(1)
8
+ end
9
+
10
+ def test_string_serializer
11
+ assert_equal '1', StringSerializer.dump(1)
12
+ assert_equal '1', StringSerializer.load('1')
13
+ end
14
+
15
+ def test_gzip_serializer
16
+ data = '0' * 500
17
+ gzip = GzipSerializer.new(NullSerializer)
18
+ zipped = gzip.dump(data)
19
+ assert zipped.size < data.size
20
+ assert_equal data, gzip.load(zipped)
21
+ end
22
+
23
+ begin
24
+ require File.expand_path("../../lib/horcrux/serializers/message_pack_serializer", __FILE__)
25
+
26
+ def test_msgpack_serializer
27
+ hash = {:a => 1, :b => {:a => 'a'}, :c => Time.utc(2000)}
28
+
29
+ data = MessagePackSerializer.dump(hash)
30
+ loaded = MessagePackSerializer.load(data)
31
+ assert_equal 1, loaded['a']
32
+ assert_equal({"a" => "a"}, loaded["b"])
33
+ assert_equal 946684800, loaded['c']
34
+ end
35
+ rescue LoadError
36
+ puts "Skipping MessagePack test. run 'gem install msgpack'"
37
+ end
38
+ end
39
+ end
40
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: horcrux
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-01 00:00:00.000000000 Z
12
+ date: 2012-01-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70345353049180 !ruby/object:Gem::Requirement
16
+ requirement: &70123004569960 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70345353049180
24
+ version_requirements: *70123004569960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: test-unit
27
- requirement: &70345353048680 !ruby/object:Gem::Requirement
27
+ requirement: &70123004569280 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,19 +32,26 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70345353048680
35
+ version_requirements: *70123004569280
36
36
  description: Simple key/value adapter library.
37
37
  email: technoweenie@gmail.com
38
38
  executables: []
39
39
  extensions: []
40
40
  extra_rdoc_files: []
41
41
  files:
42
+ - Gemfile
42
43
  - LICENSE.md
43
44
  - README.md
44
45
  - Rakefile
45
46
  - horcrux.gemspec
46
47
  - lib/horcrux.rb
48
+ - lib/horcrux/entity.rb
49
+ - lib/horcrux/serializers/gzip_serializer.rb
50
+ - lib/horcrux/serializers/message_pack_serializer.rb
51
+ - test/entity_test.rb
52
+ - test/helper.rb
47
53
  - test/memory_test.rb
54
+ - test/serializer_test.rb
48
55
  homepage: http://github.com/technoweenie/horcrux
49
56
  licenses: []
50
57
  post_install_message:
@@ -70,4 +77,7 @@ signing_key:
70
77
  specification_version: 2
71
78
  summary: Simple key/value adapter library.
72
79
  test_files:
80
+ - test/entity_test.rb
81
+ - test/helper.rb
73
82
  - test/memory_test.rb
83
+ - test/serializer_test.rb