chassis_repo 0.1.0

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.
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