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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +362 -0
- data/Rakefile +33 -0
- data/chassis.gemspec +41 -0
- data/examples/repo.rb +40 -0
- data/lib/chassis.rb +81 -0
- data/lib/chassis/array_utils.rb +8 -0
- data/lib/chassis/circuit_panel.rb +22 -0
- data/lib/chassis/core_ext/array.rb +5 -0
- data/lib/chassis/core_ext/hash.rb +5 -0
- data/lib/chassis/core_ext/string.rb +13 -0
- data/lib/chassis/delegate.rb +29 -0
- data/lib/chassis/dirty_session.rb +105 -0
- data/lib/chassis/error.rb +7 -0
- data/lib/chassis/faraday.rb +226 -0
- data/lib/chassis/form.rb +56 -0
- data/lib/chassis/hash_utils.rb +16 -0
- data/lib/chassis/heroku.rb +5 -0
- data/lib/chassis/initializable.rb +11 -0
- data/lib/chassis/logger.rb +8 -0
- data/lib/chassis/observable.rb +19 -0
- data/lib/chassis/persistence.rb +49 -0
- data/lib/chassis/rack/bouncer.rb +33 -0
- data/lib/chassis/rack/builder_shim_patch.rb +7 -0
- data/lib/chassis/rack/health_check.rb +45 -0
- data/lib/chassis/rack/instrumentation.rb +20 -0
- data/lib/chassis/rack/json_body_parser.rb +20 -0
- data/lib/chassis/rack/no_robots.rb +24 -0
- data/lib/chassis/registry.rb +30 -0
- data/lib/chassis/repo.rb +73 -0
- data/lib/chassis/repo/base_repo.rb +99 -0
- data/lib/chassis/repo/delegation.rb +78 -0
- data/lib/chassis/repo/lazy_association.rb +57 -0
- data/lib/chassis/repo/memory_repo.rb +7 -0
- data/lib/chassis/repo/null_repo.rb +64 -0
- data/lib/chassis/repo/pstore_repo.rb +54 -0
- data/lib/chassis/repo/record_map.rb +44 -0
- data/lib/chassis/repo/redis_repo.rb +55 -0
- data/lib/chassis/serializable.rb +52 -0
- data/lib/chassis/string_utils.rb +50 -0
- data/lib/chassis/version.rb +3 -0
- data/lib/chassis/web_service.rb +61 -0
- data/test/array_utils_test.rb +23 -0
- data/test/chassis_test.rb +7 -0
- data/test/circuit_panel_test.rb +22 -0
- data/test/core_ext/array_test.rb +8 -0
- data/test/core_ext/hash_test.rb +8 -0
- data/test/core_ext/string_test.rb +16 -0
- data/test/delegate_test.rb +41 -0
- data/test/dirty_session_test.rb +138 -0
- data/test/error_test.rb +12 -0
- data/test/faraday_test.rb +749 -0
- data/test/form_test.rb +29 -0
- data/test/hash_utils_test.rb +17 -0
- data/test/initializable_test.rb +22 -0
- data/test/logger_test.rb +43 -0
- data/test/observable_test.rb +27 -0
- data/test/persistence_test.rb +112 -0
- data/test/prox_test.rb +7 -0
- data/test/rack/bouncer_test.rb +42 -0
- data/test/rack/builder_patch_test.rb +36 -0
- data/test/rack/health_check_test.rb +35 -0
- data/test/rack/instrumentation_test.rb +38 -0
- data/test/rack/json_body_parser_test.rb +38 -0
- data/test/rack/no_robots_test.rb +34 -0
- data/test/registry_test.rb +26 -0
- data/test/repo/delegation_test.rb +101 -0
- data/test/repo/lazy_association_test.rb +115 -0
- data/test/repo/memory_repo_test.rb +25 -0
- data/test/repo/null_repo_test.rb +48 -0
- data/test/repo/pstore_repo_test.rb +28 -0
- data/test/repo/redis_repo_test.rb +26 -0
- data/test/repo/repo_tests.rb +120 -0
- data/test/repo_test.rb +76 -0
- data/test/serializable_test.rb +77 -0
- data/test/string_utils_test.rb +21 -0
- data/test/test_helper.rb +10 -0
- data/test/web_service_test.rb +107 -0
- metadata +426 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
module Chassis
|
2
|
+
UnknownObjectClassError = Chassis.error do
|
3
|
+
"Rename class to end in Repo or define object_class"
|
4
|
+
end
|
5
|
+
|
6
|
+
class Repo
|
7
|
+
module Delegation
|
8
|
+
def all
|
9
|
+
backend.all object_class
|
10
|
+
end
|
11
|
+
|
12
|
+
def count
|
13
|
+
backend.count object_class
|
14
|
+
end
|
15
|
+
|
16
|
+
def find(id)
|
17
|
+
backend.find object_class, id
|
18
|
+
end
|
19
|
+
|
20
|
+
def save(record)
|
21
|
+
backend.save(record)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(record)
|
25
|
+
backend.delete record
|
26
|
+
end
|
27
|
+
|
28
|
+
def first
|
29
|
+
backend.first object_class
|
30
|
+
end
|
31
|
+
|
32
|
+
def last
|
33
|
+
backend.last object_class
|
34
|
+
end
|
35
|
+
|
36
|
+
def query(selector)
|
37
|
+
backend.query object_class, selector
|
38
|
+
end
|
39
|
+
|
40
|
+
def query!(selector, &block)
|
41
|
+
backend.query! object_class, selector, &block
|
42
|
+
end
|
43
|
+
|
44
|
+
def sample
|
45
|
+
backend.sample object_class
|
46
|
+
end
|
47
|
+
|
48
|
+
def empty?
|
49
|
+
backend.empty? object_class
|
50
|
+
end
|
51
|
+
|
52
|
+
def graph(id)
|
53
|
+
backend.graph object_class, id
|
54
|
+
end
|
55
|
+
|
56
|
+
def graph_query(selector)
|
57
|
+
backend.graph_query object_class, selector
|
58
|
+
end
|
59
|
+
|
60
|
+
def lazy(id)
|
61
|
+
LazyAssociation.new self, id
|
62
|
+
end
|
63
|
+
|
64
|
+
def object_class
|
65
|
+
@object_class ||= begin
|
66
|
+
fail UnknownObjectClassError unless name
|
67
|
+
match = name.match(/^(.+)Repo$/)
|
68
|
+
fail UnknownObjectClassError unless match
|
69
|
+
StringUtils.constantize match[1]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def backend
|
74
|
+
Repo.default
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Chassis
|
2
|
+
class Repo
|
3
|
+
class LazyAssociation < Proxy
|
4
|
+
def initialize(repo, id)
|
5
|
+
@repo, @id = repo, id
|
6
|
+
end
|
7
|
+
|
8
|
+
def id
|
9
|
+
@id
|
10
|
+
end
|
11
|
+
|
12
|
+
def class
|
13
|
+
@repo.object_class
|
14
|
+
end
|
15
|
+
|
16
|
+
def repo
|
17
|
+
@repo
|
18
|
+
end
|
19
|
+
|
20
|
+
def instance_of?(klass)
|
21
|
+
self.class == klass
|
22
|
+
end
|
23
|
+
|
24
|
+
def is_a?(klass)
|
25
|
+
instance_of?(klass) || self.class > klass
|
26
|
+
end
|
27
|
+
|
28
|
+
def kind_of?(klass)
|
29
|
+
is_a? klass
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other)
|
33
|
+
if other.instance_of? self.class
|
34
|
+
other.id == id
|
35
|
+
else
|
36
|
+
other.repo == repo && other.id == id
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def eql?(other)
|
41
|
+
self == other
|
42
|
+
end
|
43
|
+
|
44
|
+
def materialize
|
45
|
+
@object ||= repo.find id
|
46
|
+
end
|
47
|
+
|
48
|
+
def __getobj__
|
49
|
+
materialize
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"#<LazyAssociation:#{object_id} @repo=#{repo} @id=#{id}>"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
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,54 @@
|
|
1
|
+
module Chassis
|
2
|
+
class PStoreRepo < BaseRepo
|
3
|
+
class AccessProxy < Chassis::Proxy
|
4
|
+
def map
|
5
|
+
__getobj__[:map] ||= Chassis::Repo::RecordMap.new
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class PStoreMap
|
10
|
+
def initialize(pstore)
|
11
|
+
@pstore = AccessProxy.new pstore
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear
|
15
|
+
pstore.transaction do
|
16
|
+
pstore.map.clear
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def all(klass)
|
21
|
+
pstore.transaction do
|
22
|
+
pstore.map.all klass
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(klass, id)
|
27
|
+
pstore.transaction do
|
28
|
+
pstore.map.get klass, id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def set(record)
|
33
|
+
pstore.transaction do
|
34
|
+
pstore.map.set record
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete(record)
|
39
|
+
pstore.transaction do
|
40
|
+
pstore.map.delete record
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def pstore
|
46
|
+
@pstore
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize(pstore)
|
51
|
+
@map = PStoreMap.new pstore
|
52
|
+
end
|
53
|
+
end
|
54
|
+
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,55 @@
|
|
1
|
+
module Chassis
|
2
|
+
class RedisRepo < Chassis::BaseRepo
|
3
|
+
class RedisMap
|
4
|
+
def initialize(redis)
|
5
|
+
@redis = redis
|
6
|
+
end
|
7
|
+
|
8
|
+
def clear
|
9
|
+
redis.del key
|
10
|
+
end
|
11
|
+
|
12
|
+
def all(klass)
|
13
|
+
read.all klass
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(klass, id)
|
17
|
+
read.get klass, id
|
18
|
+
end
|
19
|
+
|
20
|
+
def set(record)
|
21
|
+
map = read
|
22
|
+
map.set record
|
23
|
+
write map
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(record)
|
27
|
+
map = read
|
28
|
+
map.delete record
|
29
|
+
write map
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def key
|
34
|
+
'repo'
|
35
|
+
end
|
36
|
+
|
37
|
+
def redis
|
38
|
+
@redis
|
39
|
+
end
|
40
|
+
|
41
|
+
def read
|
42
|
+
value = redis.get key
|
43
|
+
value ? Marshal.load(value) : Repo::RecordMap.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def write(map)
|
47
|
+
redis.set key, Marshal.dump(map)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(redis)
|
52
|
+
@map = RedisMap.new redis
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Chassis
|
5
|
+
module Serializable
|
6
|
+
module ClassMethods
|
7
|
+
def from_hash(hash)
|
8
|
+
new.from_hash(hash)
|
9
|
+
end
|
10
|
+
|
11
|
+
def from_json(json)
|
12
|
+
from_hash JSON.load(json)
|
13
|
+
end
|
14
|
+
|
15
|
+
def from_yaml(yaml)
|
16
|
+
from_hash YAML.load(yaml)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def included(base)
|
22
|
+
base.extend ClassMethods
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def marshal_dump
|
27
|
+
fail NotImplementedError, 'subclass must implement marshal_dump'
|
28
|
+
end
|
29
|
+
|
30
|
+
def marshal_load(hash)
|
31
|
+
fail NotImplementedError, 'subclass must implement marshal_load'
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_hash
|
35
|
+
marshal_dump
|
36
|
+
end
|
37
|
+
alias_method :to_h, :to_hash
|
38
|
+
|
39
|
+
def to_json
|
40
|
+
JSON.dump marshal_dump
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_yaml
|
44
|
+
YAML.dump marshal_dump
|
45
|
+
end
|
46
|
+
|
47
|
+
def from_hash(hash)
|
48
|
+
marshal_load HashUtils.symbolize(hash)
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
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
|