chassis 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 (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +362 -0
  6. data/Rakefile +33 -0
  7. data/chassis.gemspec +41 -0
  8. data/examples/repo.rb +40 -0
  9. data/lib/chassis.rb +81 -0
  10. data/lib/chassis/array_utils.rb +8 -0
  11. data/lib/chassis/circuit_panel.rb +22 -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/dirty_session.rb +105 -0
  17. data/lib/chassis/error.rb +7 -0
  18. data/lib/chassis/faraday.rb +226 -0
  19. data/lib/chassis/form.rb +56 -0
  20. data/lib/chassis/hash_utils.rb +16 -0
  21. data/lib/chassis/heroku.rb +5 -0
  22. data/lib/chassis/initializable.rb +11 -0
  23. data/lib/chassis/logger.rb +8 -0
  24. data/lib/chassis/observable.rb +19 -0
  25. data/lib/chassis/persistence.rb +49 -0
  26. data/lib/chassis/rack/bouncer.rb +33 -0
  27. data/lib/chassis/rack/builder_shim_patch.rb +7 -0
  28. data/lib/chassis/rack/health_check.rb +45 -0
  29. data/lib/chassis/rack/instrumentation.rb +20 -0
  30. data/lib/chassis/rack/json_body_parser.rb +20 -0
  31. data/lib/chassis/rack/no_robots.rb +24 -0
  32. data/lib/chassis/registry.rb +30 -0
  33. data/lib/chassis/repo.rb +73 -0
  34. data/lib/chassis/repo/base_repo.rb +99 -0
  35. data/lib/chassis/repo/delegation.rb +78 -0
  36. data/lib/chassis/repo/lazy_association.rb +57 -0
  37. data/lib/chassis/repo/memory_repo.rb +7 -0
  38. data/lib/chassis/repo/null_repo.rb +64 -0
  39. data/lib/chassis/repo/pstore_repo.rb +54 -0
  40. data/lib/chassis/repo/record_map.rb +44 -0
  41. data/lib/chassis/repo/redis_repo.rb +55 -0
  42. data/lib/chassis/serializable.rb +52 -0
  43. data/lib/chassis/string_utils.rb +50 -0
  44. data/lib/chassis/version.rb +3 -0
  45. data/lib/chassis/web_service.rb +61 -0
  46. data/test/array_utils_test.rb +23 -0
  47. data/test/chassis_test.rb +7 -0
  48. data/test/circuit_panel_test.rb +22 -0
  49. data/test/core_ext/array_test.rb +8 -0
  50. data/test/core_ext/hash_test.rb +8 -0
  51. data/test/core_ext/string_test.rb +16 -0
  52. data/test/delegate_test.rb +41 -0
  53. data/test/dirty_session_test.rb +138 -0
  54. data/test/error_test.rb +12 -0
  55. data/test/faraday_test.rb +749 -0
  56. data/test/form_test.rb +29 -0
  57. data/test/hash_utils_test.rb +17 -0
  58. data/test/initializable_test.rb +22 -0
  59. data/test/logger_test.rb +43 -0
  60. data/test/observable_test.rb +27 -0
  61. data/test/persistence_test.rb +112 -0
  62. data/test/prox_test.rb +7 -0
  63. data/test/rack/bouncer_test.rb +42 -0
  64. data/test/rack/builder_patch_test.rb +36 -0
  65. data/test/rack/health_check_test.rb +35 -0
  66. data/test/rack/instrumentation_test.rb +38 -0
  67. data/test/rack/json_body_parser_test.rb +38 -0
  68. data/test/rack/no_robots_test.rb +34 -0
  69. data/test/registry_test.rb +26 -0
  70. data/test/repo/delegation_test.rb +101 -0
  71. data/test/repo/lazy_association_test.rb +115 -0
  72. data/test/repo/memory_repo_test.rb +25 -0
  73. data/test/repo/null_repo_test.rb +48 -0
  74. data/test/repo/pstore_repo_test.rb +28 -0
  75. data/test/repo/redis_repo_test.rb +26 -0
  76. data/test/repo/repo_tests.rb +120 -0
  77. data/test/repo_test.rb +76 -0
  78. data/test/serializable_test.rb +77 -0
  79. data/test/string_utils_test.rb +21 -0
  80. data/test/test_helper.rb +10 -0
  81. data/test/web_service_test.rb +107 -0
  82. metadata +426 -0
@@ -0,0 +1,34 @@
1
+ require_relative '../test_helper'
2
+
3
+ class NoRobotsTest < MiniTest::Unit::TestCase
4
+ include Rack::Test::Methods
5
+
6
+ class HelloWorld
7
+ def call(env)
8
+ [200, {'Content-Type' => 'text/plain' }, 'Hi']
9
+ end
10
+ end
11
+
12
+ attr_reader :log, :app
13
+
14
+ def setup
15
+ @log = StringIO.new
16
+ builder = Rack::Builder.new
17
+ builder.use Chassis::Rack::NoRobots
18
+ builder.run HelloWorld.new
19
+ @app = builder.to_app
20
+ end
21
+
22
+ def test_does_not_allow_any_robots
23
+ get '/robots.txt'
24
+
25
+ assert_includes last_response.body, 'Disallow: /'
26
+ assert_includes last_response.body, 'User Agent: *'
27
+ end
28
+
29
+ def test_allows_other_requests
30
+ get '/foo'
31
+
32
+ assert_equal 'Hi', last_response.body
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'test_helper'
2
+
3
+ class RegistryTest < MiniTest::Unit::TestCase
4
+ def test_can_get_and_set_values
5
+ registry = Chassis::Registry.new
6
+ registry[:foo] = 'bar'
7
+ assert_equal registry.fetch(:foo), 'bar'
8
+ end
9
+
10
+ def test_fails_with_an_error_if_nothing_registred
11
+ registry = Chassis::Registry.new
12
+ assert_raises Chassis::UnregisteredError do
13
+ registry.fetch :foo
14
+ end
15
+ end
16
+
17
+ def test_can_be_cleared
18
+ registry = Chassis::Registry.new
19
+ registry[:foo] = 'bar'
20
+ assert_equal registry.fetch(:foo), 'bar'
21
+ registry.clear
22
+ assert_raises Chassis::UnregisteredError do
23
+ registry.fetch :foo
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,101 @@
1
+ require_relative '../test_helper'
2
+
3
+ class RepoDelegationTest < MiniTest::Unit::TestCase
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_count_delegates_to_the_target
67
+ target.expects(:count).with(Person)
68
+ repo.count
69
+ end
70
+
71
+ def test_query_delegates_to_the_target
72
+ target.expects(:query).with(Person, :foo)
73
+ repo.query :foo
74
+ end
75
+
76
+ def test_query_bang__delegates_to_the_target
77
+ target.expects(:query!).with(Person, :foo)
78
+ repo.query! :foo
79
+ end
80
+
81
+ def test_graph_query_delegates_to_the_target
82
+ target.expects(:graph_query).with(Person, :foo)
83
+ repo.graph_query(:foo)
84
+ end
85
+
86
+ def test_sample_delegates_to_the_backend
87
+ target.expects(:sample).with(Person)
88
+ repo.sample
89
+ end
90
+
91
+ def test_empty_delegates_to_the_backend
92
+ target.expects(:empty?).with(Person)
93
+ repo.empty?
94
+ end
95
+
96
+ def test_lazy_returns_new_lazy_associations
97
+ target.expects(:find).never
98
+ person = repo.lazy 'ahawkins'
99
+ assert_equal 'ahawkins', person.id
100
+ end
101
+ end
@@ -0,0 +1,115 @@
1
+ require_relative '../test_helper'
2
+
3
+ class LazyAssociationTest < MiniTest::Unit::TestCase
4
+ class Person
5
+ attr_reader :id, :name
6
+
7
+ def initialize(id, name = nil)
8
+ @id, @name = id, name
9
+ end
10
+
11
+ def ==(other)
12
+ other.instance_of?(self.class) && other.id == id
13
+ end
14
+ end
15
+
16
+ class PersonRepo
17
+ class << self
18
+ def object_class
19
+ Person
20
+ end
21
+ end
22
+ end
23
+
24
+ attr_reader :repo
25
+
26
+ def lazy(repo, id)
27
+ Chassis::Repo::LazyAssociation.new repo, id
28
+ end
29
+
30
+ def setup
31
+ @repo = PersonRepo
32
+ end
33
+
34
+ def test_inspect_does_not_materialize
35
+ repo.expects(:find).never
36
+ lazy(repo, 1).inspect
37
+ end
38
+
39
+ def test_does_not_materialize_for_the_id
40
+ repo.expects(:find).never
41
+
42
+ association = lazy repo, 1
43
+ assert_equal 1, association.id
44
+ end
45
+
46
+ def test_reports_the_materialized_class
47
+ repo.expects(:find).never
48
+
49
+ association = lazy repo, 1
50
+ assert_equal repo.object_class, association.class
51
+ end
52
+
53
+ def test_works_with_equality
54
+ repo.expects(:find).never
55
+
56
+ a = lazy repo, 1
57
+ b = lazy repo, 1
58
+ c = lazy repo, 2
59
+
60
+ assert_equal a, b
61
+ assert_equal b, a
62
+
63
+ refute_equal c, a
64
+ refute_equal a, c
65
+
66
+ assert a.eql?(b)
67
+ assert b.eql?(a)
68
+
69
+ refute b.eql?(c)
70
+ refute c.eql?(b)
71
+ end
72
+
73
+ def test_completes_impersonates_the_materialized_class
74
+ repo.expects(:find).never
75
+
76
+ association = lazy repo, 1
77
+ assert_instance_of repo.object_class, association
78
+ assert_kind_of repo.object_class, association
79
+ assert association.is_a?(repo.object_class), "is_a? not implemented correctly"
80
+ assert_equal repo.object_class, association.class
81
+ end
82
+
83
+ def test_can_be_compared_to_materialized_objects
84
+ repo.expects(:find).never
85
+
86
+ person = Person.new 1
87
+
88
+ association = lazy repo, person.id
89
+
90
+ assert_equal association, person
91
+ assert_equal person, association
92
+ end
93
+
94
+ def test_does_not_materialize_twice_when_cached
95
+ person = Person.new 1, 'ahawkins'
96
+ repo.expects(:find).with(person.id).returns(person).once
97
+
98
+ association = lazy repo, person.id
99
+ association.materialize
100
+
101
+ assert_equal person.name, association.name
102
+
103
+ reloaded = Marshal.load(Marshal.dump(association))
104
+
105
+ assert_equal person.name, reloaded.name
106
+ end
107
+
108
+ def test_calls_to_unknown_methods_materialize_the_object
109
+ person = Person.new 1, 'ahawkins'
110
+ repo.expects(:find).with(person.id).returns(person).once
111
+
112
+ association = lazy repo, person.id
113
+ assert_equal person.name, association.name
114
+ end
115
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../test_helper'
2
+ require_relative 'repo_tests'
3
+
4
+ class MemoryRepoTest < MiniTest::Unit::TestCase
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,48 @@
1
+ require_relative '../test_helper'
2
+
3
+ class NullRepoTest < MiniTest::Unit::TestCase
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_count_returns_no
41
+ assert_equal 0, repo.count(Person)
42
+ end
43
+
44
+ def test_first_and_last_return_nil
45
+ assert_nil repo.first(Person)
46
+ assert_nil repo.first(Person)
47
+ end
48
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../test_helper'
2
+ require_relative 'repo_tests'
3
+ require 'pstore'
4
+ require 'tempfile'
5
+
6
+ class PStoreRepoTest < MiniTest::Unit::TestCase
7
+ class TestRepo < Chassis::PStoreRepo
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
+ tempfile = Tempfile.new 'pstore.test'
25
+ @repo = TestRepo.new PStore.new(tempfile.path)
26
+ super
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../test_helper'
2
+ require_relative 'repo_tests'
3
+ require 'redis'
4
+
5
+ class RedisRepoTest < MiniTest::Unit::TestCase
6
+ class TestRepo < Chassis::RedisRepo
7
+ def query_person_named(klass, selector)
8
+ all(klass).select do |person|
9
+ person.name == selector.name
10
+ end
11
+ end
12
+
13
+ def graph_query_person_named(klass, selector)
14
+ all(klass).select do |person|
15
+ person.name == selector.name
16
+ end
17
+ end
18
+ end
19
+
20
+ include RepoTests
21
+
22
+ def setup
23
+ @repo = TestRepo.new Redis.new
24
+ super
25
+ end
26
+ 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