chassis_repo 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 (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