chassis_repo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
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
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in chassis.gemspec
4
+ gemspec
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