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,100 @@
1
+ require_relative '../test_helper'
2
+
3
+ class RepoDelegationTest < Minitest::Test
4
+ class PersonRepo
5
+ extend Chassis::Repo::Delegation
6
+
7
+ def self.obj_class
8
+ Person
9
+ end
10
+ end
11
+
12
+ class SomethingSomething
13
+ extend Chassis::Repo::Delegation
14
+ end
15
+
16
+ Person = Struct.new :name
17
+
18
+ attr_reader :target, :person
19
+
20
+ def repo
21
+ PersonRepo
22
+ end
23
+
24
+ def setup
25
+ @target = mock
26
+ @person = Person.new 'ahawkins'
27
+ Chassis::Repo.stubs(:default).returns(target)
28
+ end
29
+
30
+ def test_classes_that_dont_match_on_name_fail_with_a_helpful_error
31
+ assert_raises Chassis::UnknownObjectClassError do
32
+ SomethingSomething.object_class
33
+ end
34
+ end
35
+
36
+ def test_find_delegates_to_the_target
37
+ target.expects(:find).with(Person, 1)
38
+ repo.find(1)
39
+ end
40
+
41
+ def test_delete_delegates_to_the_target
42
+ target.expects(:delete).with(person)
43
+ repo.delete(person)
44
+ end
45
+
46
+ def test_save_delegates_to_the_target
47
+ target.expects(:save).with(person)
48
+ repo.save(person)
49
+ end
50
+
51
+ def test_first_delegates_to_the_target
52
+ target.expects(:first).with(Person)
53
+ repo.first
54
+ end
55
+
56
+ def test_last_delegates_to_the_target
57
+ target.expects(:last).with(Person)
58
+ repo.last
59
+ end
60
+
61
+ def test_all_delegates_to_the_target
62
+ target.expects(:all).with(Person)
63
+ repo.all
64
+ end
65
+
66
+ def test_each_delegates_to_the_target
67
+ target.expects(:each).with(Person)
68
+ repo.each
69
+ end
70
+
71
+ def test_count_delegates_to_the_target
72
+ target.expects(:count).with(Person)
73
+ repo.count
74
+ end
75
+
76
+ def test_query_delegates_to_the_target
77
+ target.expects(:query).with(Person, :foo)
78
+ repo.query :foo
79
+ end
80
+
81
+ def test_query_bang__delegates_to_the_target
82
+ target.expects(:query!).with(Person, :foo)
83
+ repo.query! :foo
84
+ end
85
+
86
+ def test_graph_query_delegates_to_the_target
87
+ target.expects(:graph_query).with(Person, :foo)
88
+ repo.graph_query(:foo)
89
+ end
90
+
91
+ def test_sample_delegates_to_the_backend
92
+ target.expects(:sample).with(Person)
93
+ repo.sample
94
+ end
95
+
96
+ def test_empty_delegates_to_the_backend
97
+ target.expects(:empty?).with(Person)
98
+ repo.empty?
99
+ end
100
+ end
@@ -0,0 +1,52 @@
1
+ require_relative '../test_helper'
2
+ require_relative 'repo_tests'
3
+
4
+ class MaglevRepoTest < Minitest::Test
5
+ ROOT_KEY = :chassis_test
6
+
7
+ class TestRepo < Chassis::MaglevRepo
8
+ def query_person_named(klass, selector)
9
+ all(klass).select do |person|
10
+ person.name == selector.name
11
+ end
12
+ end
13
+
14
+ def graph_query_person_named(klass, selector)
15
+ all(klass).select do |person|
16
+ person.name == selector.name
17
+ end
18
+ end
19
+ end
20
+
21
+ include RepoTests
22
+
23
+ def setup
24
+ Maglev[ROOT_KEY] = {}
25
+ Maglev.commit
26
+ @repo = TestRepo.new Maglev[ROOT_KEY]
27
+ super
28
+ end
29
+
30
+ def teardown
31
+ Maglev.root.delete ROOT_KEY
32
+ Maglev.commit
33
+ end
34
+
35
+ def test_map_store_is_committed
36
+ map = @repo.send :map
37
+ store = map.send :store
38
+ refute store.nil?
39
+ assert store.committed?
40
+ end
41
+
42
+ def test_persistent_crud_operations
43
+ person = Person.new 'ahawkins'
44
+
45
+ refute person.committed?, "brand new custom objects should be transient"
46
+ repo.create person
47
+ assert person.committed?, "repo must commit saved objects"
48
+
49
+ # repo.delete(person)
50
+ # refute person.committed?, "repo must delete committed objects"
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../test_helper'
2
+ require_relative 'repo_tests'
3
+
4
+ class MemoryRepoTest < Minitest::Test
5
+ class TestRepo < Chassis::MemoryRepo
6
+ def query_person_named(klass, selector)
7
+ all(klass).select do |person|
8
+ person.name == selector.name
9
+ end
10
+ end
11
+
12
+ def graph_query_person_named(klass, selector)
13
+ all(klass).select do |person|
14
+ person.name == selector.name
15
+ end
16
+ end
17
+ end
18
+
19
+ include RepoTests
20
+
21
+ def setup
22
+ @repo = TestRepo.new
23
+ super
24
+ end
25
+ end
@@ -0,0 +1,56 @@
1
+ require_relative '../test_helper'
2
+
3
+ class NullRepoTest < Minitest::Test
4
+ Person = Struct.new :name do
5
+ attr_accessor :id
6
+ end
7
+
8
+ attr_reader :repo
9
+
10
+ def setup
11
+ @repo = Chassis::NullRepo.new
12
+ end
13
+
14
+ def test_create_sets_the_id
15
+ person = Person.new 'ahawkins'
16
+ repo.create person
17
+
18
+ assert person.id, "Repo must set the ID after creating"
19
+ end
20
+
21
+ def test_all_returns_an_empty_array
22
+ assert_equal [], repo.all(Person)
23
+ end
24
+
25
+ def test_implements_required_interface
26
+ assert_respond_to repo, :update
27
+ assert_respond_to repo, :delete
28
+ assert_respond_to repo, :find
29
+ assert_respond_to repo, :all
30
+ assert_respond_to repo, :count
31
+ assert_respond_to repo, :first
32
+ assert_respond_to repo, :last
33
+ assert_respond_to repo, :sample
34
+ assert_respond_to repo, :query
35
+ assert_respond_to repo, :graph_query
36
+ assert_respond_to repo, :graph_query
37
+ assert_respond_to repo, :clear
38
+ end
39
+
40
+ def test_query_returns_empty_array
41
+ assert_equal [ ], repo.query(Person, :selector)
42
+ end
43
+
44
+ def test_graph_query_returns_empty_array
45
+ assert_equal [ ], repo.graph_query(Person, :selector)
46
+ end
47
+
48
+ def test_count_returns_no
49
+ assert_equal 0, repo.count(Person)
50
+ end
51
+
52
+ def test_first_and_last_return_nil
53
+ assert_nil repo.first(Person)
54
+ assert_nil repo.last(Person)
55
+ end
56
+ end
@@ -0,0 +1,120 @@
1
+ module RepoTests
2
+ Person = Struct.new :name do
3
+ attr_accessor :id
4
+ end
5
+
6
+ PersonNamed = Struct.new :name
7
+ PersonFooBarBaz = Class.new
8
+
9
+ def repo
10
+ fail "test class must assign @repo" unless @repo
11
+ @repo
12
+ end
13
+
14
+ def setup
15
+ repo.clear
16
+ repo.initialize_storage
17
+ end
18
+
19
+ def test_crud_operations
20
+ assert_equal 0, repo.count(Person), "Precondition: there should be no records"
21
+
22
+ person = Person.new 'ahawkins'
23
+ repo.create person
24
+
25
+ assert person.id, "repo must set the ID after creating"
26
+
27
+ assert_equal 1, repo.count(Person)
28
+
29
+ assert_equal person, repo.find(Person, person.id)
30
+
31
+ assert_equal [person], repo.all(Person)
32
+
33
+ person.name = 'adam'
34
+ repo.update person
35
+ assert_equal 'adam', repo.find(Person, person.id).name
36
+
37
+ repo.delete(person)
38
+
39
+ assert_equal 0, repo.count(Person)
40
+ end
41
+
42
+ def test_raises_error_when_no_reecord_exists
43
+ assert_equal 0, repo.count(Person)
44
+
45
+ assert_raises Chassis::RecordNotFoundError do
46
+ repo.find Person, 1
47
+ end
48
+ end
49
+
50
+ def test_first_and_last
51
+ assert_equal 0, repo.count(Person), "Precondition: there should be no records"
52
+
53
+ adam = Person.new 'ahawkins'
54
+ peter = Person.new 'pepps'
55
+
56
+ repo.create adam
57
+ repo.create peter
58
+
59
+ assert_equal adam, repo.first(Person)
60
+ assert_equal peter, repo.last(Person)
61
+ end
62
+
63
+ def test_clear_wipes_data
64
+ assert_equal 0, repo.count(Person), "Precondition: there should be no records"
65
+
66
+ adam = Person.new 'ahawkins'
67
+ repo.create adam
68
+
69
+ refute_empty repo.all(Person)
70
+ assert_equal 1, repo.count(Person)
71
+ assert repo.find(Person, adam.id)
72
+
73
+ repo.clear
74
+
75
+ assert_empty repo.all(Person)
76
+ assert_equal 0, repo.count(Person)
77
+ end
78
+
79
+ def test_raises_an_error_when_query_not_implemented
80
+ assert_raises Chassis::QueryNotImplementedError do
81
+ repo.query Person, PersonFooBarBaz.new
82
+ end
83
+ end
84
+
85
+ def test_uses_query_method_to_implement_queries
86
+ assert_equal 0, repo.count(Person), "Precondition: there should be no records"
87
+
88
+ adam = Person.new 'ahawkins'
89
+ peter = Person.new 'pepp'
90
+
91
+ repo.create adam
92
+ repo.create peter
93
+
94
+ assert_equal 2, repo.count(Person)
95
+
96
+ query = repo.query(Person, PersonNamed.new('ahawkins'))
97
+ refute_empty query
98
+ assert_equal adam, query.first
99
+ end
100
+
101
+ def test_raises_an_error_when_a_graph_query_is_not_implemented
102
+ assert_raises Chassis::GraphQueryNotImplementedError do
103
+ repo.graph_query Person, PersonFooBarBaz.new
104
+ end
105
+ end
106
+
107
+ def test_uses_the_specific_graph_query_method_for_graph_query
108
+ assert_equal 0, repo.count(Person), "Precondition: there should be no records"
109
+
110
+ adam = Person.new 'ahawkins'
111
+ peter = Person.new 'pepp'
112
+
113
+ repo.create adam
114
+ repo.create peter
115
+
116
+ query = repo.query(Person, PersonNamed.new('ahawkins'))
117
+ refute_empty query
118
+ assert_equal adam, query.first
119
+ end
120
+ end
data/test/repo_test.rb ADDED
@@ -0,0 +1,76 @@
1
+ require_relative 'test_helper'
2
+
3
+ class RepoTest < Minitest::Test
4
+ class TestAdapter < Chassis::MemoryRepo
5
+ def query_test_empty_array_query(klass, q)
6
+ [ ]
7
+ end
8
+
9
+ def query_test_nil_query(klass, q)
10
+ nil
11
+ end
12
+
13
+ def query_person_by_name(klass, q)
14
+ all(klass).find do |record|
15
+ record.name == q.name
16
+ end
17
+ end
18
+
19
+ def update(record)
20
+ record.name = 'updated'
21
+ end
22
+ end
23
+
24
+ CustomError = Class.new RuntimeError
25
+ TestEmptyArrayQuery = Struct.new :foo
26
+ TestNilQuery = Struct.new :foo
27
+ PersonByName = Struct.new :name
28
+ Person = Struct.new :id, :name
29
+
30
+ def repo
31
+ Chassis.repo
32
+ end
33
+
34
+ def setup
35
+ repo.register :test, TestAdapter.new
36
+ repo.use :test
37
+ end
38
+
39
+ def test_query_bang_raises_an_exception_if_empty
40
+ assert_raises Chassis::NoQueryResultError do
41
+ repo.query! Person, TestEmptyArrayQuery.new(:foo)
42
+ end
43
+ end
44
+
45
+ def test_query_bang_raises_an_exception_if_nil
46
+ assert_raises Chassis::NoQueryResultError do
47
+ repo.query! Person, TestNilQuery.new(:foo)
48
+ end
49
+ end
50
+
51
+ def test_query_bang_can_take_a_block_to_customize_exception
52
+ assert_raises CustomError do
53
+ repo.query! Person, TestNilQuery.new(:foo) do |klass, selector|
54
+ fail CustomError
55
+ end
56
+ end
57
+ end
58
+
59
+ def test_query_bang_works_when_a_non_array_object_is_returned
60
+ person = Person.new nil, 'ahawkins'
61
+ repo.save person
62
+ assert_equal person, repo.query!(Person, PersonByName.new('ahawkins'))
63
+ end
64
+
65
+ def test_save_creates_records_without_ids
66
+ person = Person.new nil, 'ahawkins'
67
+ repo.save person
68
+ assert person.id
69
+ end
70
+
71
+ def test_save_updates_records_otherwise
72
+ person = Person.new 1, 'ahawkins'
73
+ repo.save person
74
+ assert_equal 'updated', person.name
75
+ end
76
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'test_helper'
2
+
3
+ class StringUtilsTest < Minitest::Test
4
+ def utils
5
+ Chassis::StringUtils
6
+ end
7
+
8
+ def test_underscore
9
+ result = utils.underscore 'FooBar'
10
+ assert_equal 'foo_bar', result
11
+ end
12
+
13
+ def test_demodulize
14
+ result = utils.demodulize 'Foo::Bar::Baz'
15
+ assert_equal 'Baz', result
16
+ end
17
+
18
+ def test_constantize
19
+ assert_equal utils, utils.constantize('Chassis::StringUtils')
20
+ end
21
+ end