chassis_repo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +47 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +42 -0
- data/LICENSE.txt +22 -0
- data/README.md +118 -0
- data/Rakefile +34 -0
- data/chassis_repo.gemspec +33 -0
- data/examples/maglev_repo.rb +56 -0
- data/examples/repo.rb +40 -0
- data/lib/chassis.rb +34 -0
- data/lib/chassis/array_utils.rb +8 -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/error.rb +7 -0
- data/lib/chassis/hash_utils.rb +16 -0
- data/lib/chassis/initializable.rb +3 -0
- data/lib/chassis/logger.rb +8 -0
- data/lib/chassis/persistence.rb +84 -0
- data/lib/chassis/repo.rb +71 -0
- data/lib/chassis/repo/base_repo.rb +99 -0
- data/lib/chassis/repo/delegation.rb +82 -0
- data/lib/chassis/repo/maglev_repo.rb +51 -0
- data/lib/chassis/repo/memory_repo.rb +7 -0
- data/lib/chassis/repo/null_repo.rb +64 -0
- data/lib/chassis/repo/record_map.rb +44 -0
- data/lib/chassis/string_utils.rb +50 -0
- data/lib/chassis/version.rb +3 -0
- data/test/array_utils_test.rb +23 -0
- data/test/chassis_test.rb +7 -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/error_test.rb +12 -0
- data/test/hash_utils_test.rb +17 -0
- data/test/initializable_test.rb +7 -0
- data/test/logger_test.rb +43 -0
- data/test/persistence_test.rb +112 -0
- data/test/repo/delegation_test.rb +100 -0
- data/test/repo/maglev_repo_test.rb +52 -0
- data/test/repo/memory_repo_test.rb +25 -0
- data/test/repo/null_repo_test.rb +56 -0
- data/test/repo/repo_tests.rb +120 -0
- data/test/repo_test.rb +76 -0
- data/test/string_utils_test.rb +21 -0
- data/test/test_helper.rb +13 -0
- metadata +274 -0
data/.gitignore
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Vagrant #
|
2
|
+
###################
|
3
|
+
.vagrant
|
4
|
+
|
5
|
+
# Compiled source #
|
6
|
+
###################
|
7
|
+
*.com
|
8
|
+
*.class
|
9
|
+
*.dll
|
10
|
+
*.exe
|
11
|
+
*.o
|
12
|
+
*.so
|
13
|
+
|
14
|
+
# Packages #
|
15
|
+
############
|
16
|
+
# it's better to unpack these files and commit the raw source
|
17
|
+
# git has its own built in compression methods
|
18
|
+
*.7z
|
19
|
+
*.dmg
|
20
|
+
*.gz
|
21
|
+
*.iso
|
22
|
+
*.jar
|
23
|
+
*.rar
|
24
|
+
*.tar
|
25
|
+
*.zip
|
26
|
+
|
27
|
+
# Logs and databases #
|
28
|
+
######################
|
29
|
+
*.log
|
30
|
+
*.sql
|
31
|
+
*.sqlite
|
32
|
+
|
33
|
+
# OS generated files #
|
34
|
+
######################
|
35
|
+
.DS_Store
|
36
|
+
.DS_Store?
|
37
|
+
._*
|
38
|
+
.Spotlight-V100
|
39
|
+
.Trashes
|
40
|
+
ehthumbs.db
|
41
|
+
Thumbs.db
|
42
|
+
|
43
|
+
nsqd.*.dat
|
44
|
+
|
45
|
+
# Rubygems #
|
46
|
+
############
|
47
|
+
pkg
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
chassis (0.1.0)
|
5
|
+
interchange
|
6
|
+
lift
|
7
|
+
logger-better
|
8
|
+
prox
|
9
|
+
tnt
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: http://rubygems.org/
|
13
|
+
specs:
|
14
|
+
ansi (1.4.3)
|
15
|
+
builder (3.2.2)
|
16
|
+
interchange (0.1.0)
|
17
|
+
lift (0.1.0)
|
18
|
+
logger-better (0.2.1)
|
19
|
+
metaclass (0.0.4)
|
20
|
+
minitest (5.4.2)
|
21
|
+
minitest-reporters (1.0.7)
|
22
|
+
ansi
|
23
|
+
builder
|
24
|
+
minitest (>= 5.0)
|
25
|
+
ruby-progressbar
|
26
|
+
mocha (1.1.0)
|
27
|
+
metaclass (~> 0.0.1)
|
28
|
+
prox (0.0.1)
|
29
|
+
rake (10.4.2)
|
30
|
+
ruby-progressbar (1.7.0)
|
31
|
+
tnt (0.1.0)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
bundler
|
38
|
+
chassis!
|
39
|
+
minitest (= 5.4.2)
|
40
|
+
minitest-reporters
|
41
|
+
mocha
|
42
|
+
rake
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 ahawkins
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# ChassisRepo
|
2
|
+
|
3
|
+
ChassisRepo is the repo part of the [Chassis project](https://github.com/ahawkins/chassis) which will be used for MagLev.
|
4
|
+
|
5
|
+
## Support Libraries
|
6
|
+
|
7
|
+
Chassis is implemented with help from a few smaller libraries. A
|
8
|
+
unified interface if you do not want to know about such things.
|
9
|
+
|
10
|
+
* Errors with [TNT](https://github.com/ahawkins/tnt)
|
11
|
+
* Object initialization with [Lift](https://github.com/ahawkins/lift)
|
12
|
+
* Interchangeable objects with [Interchange](https://github.com/ahawkins/interchange)
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'chassis_repo'
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install chassis_repo
|
27
|
+
|
28
|
+
|
29
|
+
## Data Access
|
30
|
+
|
31
|
+
Chassis includes a
|
32
|
+
[repository](http://martinfowler.com/eaaCatalog/repository.html) using
|
33
|
+
the query pattern as well. The repository pattern is perfect because
|
34
|
+
it does not require knowledge about your persistence layer. It is the
|
35
|
+
access layer. A null, in-memory, and Redis adapter are included. You
|
36
|
+
can subclass these adapters to make your own.
|
37
|
+
`Chassis::Repo::Delegation` can be included in other classes to
|
38
|
+
delegate to the repository.
|
39
|
+
|
40
|
+
Here's an example:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class CustomerRepo
|
44
|
+
extend Chassis::Repo::Delegation
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Now there are CRUD methods available on `CustomerRepo` that delegate
|
49
|
+
to the repository for `Customer` objects. `Chassis::Persistence` can
|
50
|
+
be included in any object. It will make the object compatible with
|
51
|
+
the matching repo.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class Customer
|
55
|
+
include Chassis::Persistence
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
Now `Customer` responds to `id`, `save`, and `repo`. `repo` looks for
|
60
|
+
a repository class matching the class name (e.g. `CustomerRepo`).
|
61
|
+
Override as you see if.
|
62
|
+
|
63
|
+
More on my blog
|
64
|
+
[here](http://hawkins.io/2014/01/pesistence_with_repository_and_query_patterns/).
|
65
|
+
|
66
|
+
|
67
|
+
## Chassis::DirtySession
|
68
|
+
|
69
|
+
A proxy object used to track assignments. Wrap an object in a dirty
|
70
|
+
session to see what changed and what it changed to.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
Person = Struct.new :name
|
74
|
+
|
75
|
+
adam = Person.new 'adam'
|
76
|
+
|
77
|
+
session = Chassis::DirtySession.new adam
|
78
|
+
session.clean? # => true
|
79
|
+
session.dirty? # => false
|
80
|
+
|
81
|
+
session.name = 'Adman'
|
82
|
+
|
83
|
+
session.dirty? # => true
|
84
|
+
session.clean? # => false
|
85
|
+
|
86
|
+
session.named_changed? # => true
|
87
|
+
session.changed # => set of values changed
|
88
|
+
session.new_values # => { name: 'Adman' }
|
89
|
+
session.original_values # => { name: 'adam' }
|
90
|
+
|
91
|
+
session.reset! # reset everything back to normal
|
92
|
+
```
|
93
|
+
|
94
|
+
## Chassis::Logger
|
95
|
+
|
96
|
+
Chassis includes the `logger-better` gem to refine the standard
|
97
|
+
library logger. `Chassis::Logger` default the `logdev` argument to
|
98
|
+
`Chassis.stream`. This gives a unified place to assign all output.
|
99
|
+
The log level can also be controlled by the `LOG_LEVEL` environment
|
100
|
+
variable. This makes it possible to restart/boot the application with
|
101
|
+
a new log level without redeploying code.
|
102
|
+
|
103
|
+
## Chassis::Observable
|
104
|
+
|
105
|
+
A very simple implementation of the observer pattern. It is different
|
106
|
+
from the standard library implementation for two reasons:
|
107
|
+
|
108
|
+
* you don't need to call `changed` for `notify_observers` to work.
|
109
|
+
* `notify_obsevers` includes `self` as first argument to all observers
|
110
|
+
* there is only the `add_observer` method.
|
111
|
+
|
112
|
+
## Contributing
|
113
|
+
|
114
|
+
1. Fork it
|
115
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
116
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
117
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
118
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require "rake/testtask"
|
5
|
+
require "stringio"
|
6
|
+
|
7
|
+
def capture_stdout
|
8
|
+
out = StringIO.new
|
9
|
+
$stdout = out
|
10
|
+
yield
|
11
|
+
return out
|
12
|
+
ensure
|
13
|
+
$stdout = STDOUT
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Run examples"
|
17
|
+
task :examples do
|
18
|
+
root = File.dirname __FILE__
|
19
|
+
Dir["#{root}/examples/*.rb"].each do |example|
|
20
|
+
capture_stdout do
|
21
|
+
require example
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
namespace :test do
|
27
|
+
Rake::TestTask.new(:all) do |t|
|
28
|
+
t.test_files = FileList['test/**/*_test.rb']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
task test: ["test:all", "examples"]
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "chassis/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "chassis_repo"
|
8
|
+
spec.version = Chassis::VERSION
|
9
|
+
spec.authors = ["ahawkins"]
|
10
|
+
spec.email = ["adam@hawkins.io"]
|
11
|
+
spec.description = %q{A collection of modules and helpers for building mantainable Ruby applications}
|
12
|
+
spec.summary = %q{}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "interchange"
|
22
|
+
spec.add_dependency "tnt"
|
23
|
+
spec.add_dependency "lift"
|
24
|
+
|
25
|
+
spec.add_dependency "prox"
|
26
|
+
spec.add_dependency "logger-better"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
spec.add_development_dependency "mocha"
|
31
|
+
spec.add_development_dependency "minitest", "= 5.4.2"
|
32
|
+
spec.add_development_dependency "minitest-reporters"
|
33
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative '../lib/chassis'
|
2
|
+
|
3
|
+
Maglev.abort
|
4
|
+
|
5
|
+
ROOT_KEY = :chassis_example
|
6
|
+
Maglev[ROOT_KEY] = {}
|
7
|
+
Maglev.commit
|
8
|
+
|
9
|
+
Chassis.repo.register :maglev, Chassis::MaglevRepo.new(Maglev[ROOT_KEY])
|
10
|
+
Chassis.repo.use :maglev
|
11
|
+
|
12
|
+
class Post
|
13
|
+
attr_accessor :id, :title, :text
|
14
|
+
end
|
15
|
+
Post.maglev_persistable
|
16
|
+
|
17
|
+
repo = Chassis.repo
|
18
|
+
|
19
|
+
puts repo.empty? Post #=> true
|
20
|
+
|
21
|
+
post = Post.new
|
22
|
+
post.title = 'Such Repos'
|
23
|
+
post.text = 'Very wow. Much design.'
|
24
|
+
|
25
|
+
repo.save post
|
26
|
+
|
27
|
+
puts post.id #=> 1
|
28
|
+
|
29
|
+
found_post = repo.find Post, post.id
|
30
|
+
found_post == post #=> true (no difference between objects in memory)
|
31
|
+
|
32
|
+
post.title = 'Such updates'
|
33
|
+
post.text = 'Very easy. Wow'
|
34
|
+
|
35
|
+
repo.save post
|
36
|
+
|
37
|
+
repo.find(Post, post.id).text #=> 'Very easy. Wow.'
|
38
|
+
|
39
|
+
class PostRepo
|
40
|
+
extend Chassis::Repo::Delegation
|
41
|
+
end
|
42
|
+
|
43
|
+
post = Post.new
|
44
|
+
post.title = 'Such Repos'
|
45
|
+
post.text = 'Very wow. Much design.'
|
46
|
+
|
47
|
+
PostRepo.save post
|
48
|
+
|
49
|
+
post = PostRepo.find post.id
|
50
|
+
post.text #=> 'Very Easy. Wow'
|
51
|
+
|
52
|
+
PostRepo.all #=> [post]
|
53
|
+
# etc
|
54
|
+
|
55
|
+
PostRepo.delete post
|
56
|
+
PostRepo.empty? #=> true
|
data/examples/repo.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'chassis'
|
2
|
+
|
3
|
+
class Post
|
4
|
+
attr_accessor :id, :title, :text
|
5
|
+
end
|
6
|
+
|
7
|
+
repo = Chassis::Repo.default
|
8
|
+
|
9
|
+
puts repo.empty? Post #=> true
|
10
|
+
|
11
|
+
post = Post.new
|
12
|
+
post.title = 'Such Repos'
|
13
|
+
post.text = 'Very wow. Much design.'
|
14
|
+
|
15
|
+
repo.save post
|
16
|
+
|
17
|
+
puts post.id #=> 1
|
18
|
+
|
19
|
+
found_post = repo.find Post, post.id
|
20
|
+
found_post == post #=> true (no difference between objects in memory)
|
21
|
+
|
22
|
+
post.title = 'Such updates'
|
23
|
+
post.text = 'Very easy. Wow'
|
24
|
+
|
25
|
+
repo.save post
|
26
|
+
|
27
|
+
repo.find(Post, post.id).text #=> 'Very easy. Wow.'
|
28
|
+
|
29
|
+
class PostRepo
|
30
|
+
extend Chassis::Repo::Delegation
|
31
|
+
end
|
32
|
+
|
33
|
+
post = PostRepo.find post.id
|
34
|
+
post.text #=> 'Very Easy. Wow'
|
35
|
+
|
36
|
+
PostRepo.all #=> [post]
|
37
|
+
# etc
|
38
|
+
|
39
|
+
PostRepo.delete post
|
40
|
+
PostRepo.empty? #=> true
|
data/lib/chassis.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'interchange'
|
4
|
+
require 'tnt'
|
5
|
+
require 'lift'
|
6
|
+
require 'logger-better'
|
7
|
+
require 'prox'
|
8
|
+
|
9
|
+
module Chassis
|
10
|
+
Proxy = Prox
|
11
|
+
class << self
|
12
|
+
def stream
|
13
|
+
@stream
|
14
|
+
end
|
15
|
+
|
16
|
+
def stream=(stream)
|
17
|
+
@stream = stream
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require_relative 'chassis/hash_utils'
|
23
|
+
require_relative 'chassis/string_utils'
|
24
|
+
require_relative 'chassis/array_utils'
|
25
|
+
require_relative 'chassis/error'
|
26
|
+
require_relative 'chassis/logger'
|
27
|
+
require_relative 'chassis/persistence'
|
28
|
+
require_relative 'chassis/initializable'
|
29
|
+
require_relative 'chassis/repo'
|
30
|
+
require_relative 'chassis/delegate'
|
31
|
+
|
32
|
+
Chassis.repo.register :memory, Chassis::MemoryRepo.new
|
33
|
+
Chassis.repo.register :null, Chassis::NullRepo.new
|
34
|
+
Chassis.repo.use :memory
|