chassis 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|