chassis_repo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +47 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +42 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +118 -0
  6. data/Rakefile +34 -0
  7. data/chassis_repo.gemspec +33 -0
  8. data/examples/maglev_repo.rb +56 -0
  9. data/examples/repo.rb +40 -0
  10. data/lib/chassis.rb +34 -0
  11. data/lib/chassis/array_utils.rb +8 -0
  12. data/lib/chassis/core_ext/array.rb +5 -0
  13. data/lib/chassis/core_ext/hash.rb +5 -0
  14. data/lib/chassis/core_ext/string.rb +13 -0
  15. data/lib/chassis/delegate.rb +29 -0
  16. data/lib/chassis/error.rb +7 -0
  17. data/lib/chassis/hash_utils.rb +16 -0
  18. data/lib/chassis/initializable.rb +3 -0
  19. data/lib/chassis/logger.rb +8 -0
  20. data/lib/chassis/persistence.rb +84 -0
  21. data/lib/chassis/repo.rb +71 -0
  22. data/lib/chassis/repo/base_repo.rb +99 -0
  23. data/lib/chassis/repo/delegation.rb +82 -0
  24. data/lib/chassis/repo/maglev_repo.rb +51 -0
  25. data/lib/chassis/repo/memory_repo.rb +7 -0
  26. data/lib/chassis/repo/null_repo.rb +64 -0
  27. data/lib/chassis/repo/record_map.rb +44 -0
  28. data/lib/chassis/string_utils.rb +50 -0
  29. data/lib/chassis/version.rb +3 -0
  30. data/test/array_utils_test.rb +23 -0
  31. data/test/chassis_test.rb +7 -0
  32. data/test/core_ext/array_test.rb +8 -0
  33. data/test/core_ext/hash_test.rb +8 -0
  34. data/test/core_ext/string_test.rb +16 -0
  35. data/test/delegate_test.rb +41 -0
  36. data/test/error_test.rb +12 -0
  37. data/test/hash_utils_test.rb +17 -0
  38. data/test/initializable_test.rb +7 -0
  39. data/test/logger_test.rb +43 -0
  40. data/test/persistence_test.rb +112 -0
  41. data/test/repo/delegation_test.rb +100 -0
  42. data/test/repo/maglev_repo_test.rb +52 -0
  43. data/test/repo/memory_repo_test.rb +25 -0
  44. data/test/repo/null_repo_test.rb +56 -0
  45. data/test/repo/repo_tests.rb +120 -0
  46. data/test/repo_test.rb +76 -0
  47. data/test/string_utils_test.rb +21 -0
  48. data/test/test_helper.rb +13 -0
  49. metadata +274 -0
@@ -0,0 +1,51 @@
1
+ module Chassis
2
+ class MaglevRepo < BaseRepo
3
+ class MaglevMap
4
+ def initialize store
5
+ @store = store
6
+ end
7
+
8
+ def set(record)
9
+ record_map(record)[record.id] = record
10
+ Maglev.commit
11
+ end
12
+
13
+ def get(klass, id)
14
+ class_map(klass).fetch id do
15
+ fail RecordNotFoundError.new(klass, id)
16
+ end
17
+ end
18
+
19
+ def delete(record)
20
+ record_map(record).delete record.id
21
+ Maglev.commit
22
+ end
23
+
24
+ def all(klass)
25
+ class_map(klass).values
26
+ end
27
+
28
+ def clear
29
+ store.clear
30
+ Maglev.commit
31
+ end
32
+
33
+ private
34
+ def store
35
+ @store
36
+ end
37
+
38
+ def class_map(klass)
39
+ store[klass] ||= { }
40
+ end
41
+
42
+ def record_map(record)
43
+ class_map record.class
44
+ end
45
+ end
46
+
47
+ def initialize store
48
+ @map = MaglevMap.new store
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ module Chassis
2
+ class MemoryRepo < BaseRepo
3
+ def initialize
4
+ @map = Repo::RecordMap.new
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,64 @@
1
+ module Chassis
2
+ class NullRepo
3
+ def initialize
4
+ @counter = 0
5
+ end
6
+
7
+ def create(record)
8
+ @counter = @counter + 1
9
+ record.id ||= @counter
10
+ end
11
+
12
+ def update(*)
13
+
14
+ end
15
+
16
+ def delete(*)
17
+
18
+ end
19
+
20
+ def find(*)
21
+
22
+ end
23
+
24
+ def first(*)
25
+
26
+ end
27
+
28
+ def last(*)
29
+
30
+ end
31
+
32
+ def sample(*)
33
+
34
+ end
35
+
36
+ def clear
37
+
38
+ end
39
+
40
+ def count(*)
41
+ 0
42
+ end
43
+
44
+ def first(*)
45
+
46
+ end
47
+
48
+ def all(klass)
49
+ [ ]
50
+ end
51
+
52
+ def query(*)
53
+ [ ]
54
+ end
55
+
56
+ def graph_query(*)
57
+ [ ]
58
+ end
59
+
60
+ def initialize_storage
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ module Chassis
2
+ class Repo
3
+ class RecordMap
4
+ def initialize
5
+ @hash = { }
6
+ end
7
+
8
+ def set(record)
9
+ record_map(record)[record.id] = record
10
+ end
11
+
12
+ def get(klass, id)
13
+ class_map(klass).fetch id do
14
+ fail RecordNotFoundError.new(klass, id)
15
+ end
16
+ end
17
+
18
+ def delete(record)
19
+ record_map(record).delete record.id
20
+ end
21
+
22
+ def all(klass)
23
+ class_map(klass).values
24
+ end
25
+
26
+ def clear
27
+ hash.clear
28
+ end
29
+
30
+ private
31
+ def hash
32
+ @hash
33
+ end
34
+
35
+ def class_map(klass)
36
+ hash[klass] ||= { }
37
+ end
38
+
39
+ def record_map(record)
40
+ class_map record.class
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,50 @@
1
+ module Chassis
2
+ module StringUtils
3
+ def demodulize(path)
4
+ path = path.to_s
5
+ if i = path.rindex('::')
6
+ path[(i+2)..-1]
7
+ else
8
+ path
9
+ end
10
+ end
11
+ module_function :demodulize
12
+
13
+ def underscore(camel_cased_word)
14
+ word = camel_cased_word.to_s.gsub('::', '/')
15
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
16
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
17
+ word.tr!("-", "_")
18
+ word.downcase!
19
+ word
20
+ end
21
+ module_function :underscore
22
+
23
+ def constantize(camel_cased_word)
24
+ names = camel_cased_word.split('::')
25
+ names.shift if names.empty? || names.first.empty?
26
+
27
+ names.inject(Object) do |constant, name|
28
+ if constant == Object
29
+ constant.const_get(name)
30
+ else
31
+ candidate = constant.const_get(name)
32
+ next candidate if constant.const_defined?(name, false)
33
+ next candidate unless Object.const_defined?(name)
34
+
35
+ # Go down the ancestors to check it it's owned
36
+ # directly before we reach Object or the end of ancestors.
37
+ constant = constant.ancestors.inject do |const, ancestor|
38
+ break const if ancestor == Object
39
+ break ancestor if ancestor.const_defined?(name, false)
40
+ const
41
+ end
42
+
43
+ # owner is in Object, so raise
44
+ constant.const_get(name, false)
45
+ end
46
+ end
47
+ end
48
+ module_function :constantize
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module Chassis
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'test_helper'
2
+
3
+ class ArrayUtilsTest < Minitest::Test
4
+ def utils
5
+ Chassis::ArrayUtils
6
+ end
7
+
8
+ def test_extract_options_removes_options_hash_if_present
9
+ args = ['foo', { bar: 'baz' }]
10
+ options = utils.extract_options! args
11
+
12
+ assert_equal({ bar: 'baz' }, options)
13
+ assert_equal(%w(foo), args)
14
+ end
15
+
16
+ def test_extract_options_does_nothing_if_no_options
17
+ args = %w(foo)
18
+ options = utils.extract_options! args
19
+
20
+ assert_equal({}, options)
21
+ assert_equal(%w(foo), args)
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'test_helper'
2
+
3
+ class ChassisTest < Minitest::Test
4
+ def test_it_should_define_a_version
5
+ assert Chassis::VERSION
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ require_relative '../test_helper'
2
+ require 'chassis/core_ext/array'
3
+
4
+ class ArrayCoreExtTest < Minitest::Test
5
+ def test_extract_options!
6
+ assert_equal({}, [].extract_options!)
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ require_relative '../test_helper'
2
+ require 'chassis/core_ext/hash'
3
+
4
+ class HashCoreExtTest < Minitest::Test
5
+ def test_symbolize
6
+ assert_equal({ foo: 'bar' }, { 'foo' => 'bar' }.symbolize)
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ require_relative '../test_helper'
2
+ require 'chassis/core_ext/string'
3
+
4
+ class StringCoreExtTest < Minitest::Test
5
+ def test_underscore
6
+ assert_equal('foo_bar', 'FooBar'.underscore)
7
+ end
8
+
9
+ def test_demodulize
10
+ assert_equal('Bar', 'Foo::Bar'.demodulize)
11
+ end
12
+
13
+ def test_constantize
14
+ assert_equal self.class, self.class.name.constantize
15
+ end
16
+ end
@@ -0,0 +1,41 @@
1
+ require_relative 'test_helper'
2
+
3
+ class DelegateTest < Minitest::Test
4
+ class Delegator
5
+ include Chassis.delegate(:add, to: :object)
6
+
7
+ attr_reader :object
8
+
9
+ def initialize(object)
10
+ @object = object
11
+ end
12
+ end
13
+
14
+ def test_methods_are_delegated
15
+ delegate = Class.new do
16
+ def add(number)
17
+ number + 5
18
+ end
19
+ end.new
20
+
21
+ delegator = Delegator.new delegate
22
+
23
+ assert_equal 10, delegator.add(5)
24
+ end
25
+
26
+ def test_raises_an_error_if_no_object_to_delegate_to
27
+ delegator = Delegator.new nil
28
+
29
+ assert_raises Chassis::DelegationError do
30
+ delegator.add 1
31
+ end
32
+ end
33
+
34
+ def test_fails_with_an_error_if_nothing_to_delegate_to
35
+ assert_raises ArgumentError do
36
+ Class.new do
37
+ include Chassis.delegate(:foo, :bar)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'test_helper'
2
+
3
+ class ErrorTest < Minitest::Test
4
+ def test_delegates_to_tnt
5
+ klass = Chassis.error do |foo|
6
+ "#{foo} bar"
7
+ end
8
+
9
+ error = klass.new "Adam"
10
+ assert_equal "Adam bar", error.message
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'test_helper'
2
+
3
+ class HashUtilsTest < Minitest::Test
4
+ def utils
5
+ Chassis::HashUtils
6
+ end
7
+
8
+ def test_symbolize_keys_converts_keys_to_symbols
9
+ result = utils.symbolize({ 'foo' => 'bar' })
10
+ assert_equal({ foo: 'bar'}, result)
11
+ end
12
+
13
+ def test_symbolize_keys_recursises_into_objects
14
+ result = utils.symbolize({ 'foo' => { 'bar' => 'baz' }})
15
+ assert_equal({ foo: { bar: 'baz' }}, result)
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'test_helper'
2
+
3
+ class InitializableTest < Minitest::Test
4
+ def test_uses_lift
5
+ assert_equal Lift, Chassis::Initializable
6
+ end
7
+ end
@@ -0,0 +1,43 @@
1
+ require_relative 'test_helper'
2
+
3
+ class LoggerTest < Minitest::Test
4
+ #def test_log_level_defaults_to_env_variable
5
+ # with_env :warn do
6
+ # logger = Chassis::Logger.new $stdout
7
+ # assert_equal Logger::WARN, logger.level
8
+ # end
9
+ #end
10
+
11
+ def test_log_deafults_debug_without_env_variable
12
+ refute ENV['LOG_LEVEL'], "Precondition: LOG_LEVEL must be blank"
13
+
14
+ logger = Chassis::Logger.new $stdout
15
+ assert_equal Logger::DEBUG, logger.level
16
+ end
17
+
18
+ def test_log_dev_defaults_to_chassis_stream
19
+ Chassis.stream = StringIO.new
20
+
21
+ logger = Chassis::Logger.new
22
+ logger.debug 'test'
23
+
24
+ Chassis.stream.rewind
25
+ content = Chassis.stream.read
26
+
27
+ assert_includes content, 'test'
28
+ end
29
+
30
+ private
31
+ def with_env(name)
32
+ original_env = ENV['LOG_LEVEL']
33
+ ENV['LOG_LEVEL'] = name.to_s
34
+
35
+ yield
36
+ ensure
37
+ if original_env
38
+ ENV['LOG_LEVEL'] = original_env
39
+ else
40
+ ENV.delete 'LOG_LEVEL'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,112 @@
1
+ require_relative "test_helper"
2
+
3
+ class PersistenceTest < Minitest::Test
4
+ class FakeRepo
5
+
6
+ end
7
+
8
+ class Model
9
+ include Chassis::Persistence
10
+
11
+ class << self
12
+ def repo
13
+ FakeRepo
14
+ end
15
+ end
16
+ end
17
+
18
+ def test_models_can_be_initialize_with_a_hash
19
+ assert_includes Model.ancestors, Chassis::Initializable
20
+ end
21
+
22
+ def test_class_has_a_create_factory
23
+ FakeRepo.expects(:save)
24
+ Model.create id: 5
25
+ end
26
+
27
+ def test_delegates_save_to_the_repo
28
+ model = Model.new id: 5
29
+ FakeRepo.expects(:save).with(model)
30
+ model.save
31
+ end
32
+
33
+ def test_delegates_delete_to_the_repo
34
+ model = Model.new id: 5
35
+ FakeRepo.expects(:delete).with(model)
36
+ model.delete
37
+ end
38
+
39
+ def test_is_new_record_when_id_is_nil
40
+ model = Model.new id: 5
41
+ refute model.new_record?
42
+
43
+ model.id = nil
44
+ assert model.new_record?
45
+ end
46
+
47
+ def test_implements_double_equals
48
+ m1 = Model.new(id: 1)
49
+ m2 = Model.new(id: 2)
50
+ m3 = Model.new(id: 1)
51
+
52
+ assert (m1 == m1)
53
+ assert (m1 == m3)
54
+ refute (m1 == m2)
55
+ end
56
+
57
+ def test_implements_triple_equals
58
+ m1 = Model.new(id: 1)
59
+ m2 = Model.new(id: 2)
60
+ m3 = Model.new(id: 1)
61
+
62
+ assert (m1 === m1)
63
+ assert (m1 === m3)
64
+ refute (m1 === m2)
65
+ end
66
+
67
+ def test_implements_eql?
68
+ m1 = Model.new(id: 1)
69
+ m2 = Model.new(id: 2)
70
+ m3 = Model.new(id: 1)
71
+
72
+ assert (m1 == m1)
73
+ assert (m1 == m3)
74
+ refute (m1 == m2)
75
+ end
76
+
77
+ def test_acts_has_a_hash_key
78
+ m1 = Model.new(id: 1)
79
+ m2 = Model.new(id: 2)
80
+ m3 = Model.new(id: 1)
81
+
82
+ hash = { }
83
+ hash[m1] = 'm1'
84
+
85
+ assert_includes hash, m1
86
+ assert_includes hash, m3
87
+ refute_includes hash, m2
88
+ end
89
+
90
+ def test_acts_well_in_arrays
91
+ m1 = Model.new(id: 1)
92
+ m2 = Model.new(id: 2)
93
+ m3 = Model.new(id: 1)
94
+
95
+ array = [ m1 ]
96
+
97
+ assert_includes array, m1
98
+ assert_includes array, m3
99
+ refute_includes array, m2
100
+ end
101
+
102
+ def test_save_takes_a_block_of_changes
103
+ FakeRepo.expects(:save)
104
+
105
+ model = Model.new id: 5
106
+ model.save do |m|
107
+ m.id = 6
108
+ end
109
+
110
+ assert_equal 6, model.id
111
+ end
112
+ end