vorpal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NjI3OWU1NDlmYjJjZjAyOTZkZmU5NThmZjJjMDI4NzVlNGE1MjFjZA==
5
+ data.tar.gz: !binary |-
6
+ ZGJlYjljY2ViYTk0MzdhMWYxZjEyMWU5NmZjMGUxMzU2YTI3MzRmMw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZDAzMmU1MmVlNzYwYTlmNTk0NGIxMGExZTgwMDIwN2I5YzQ4NTNiNTQ5ZGRi
10
+ ZjRiYjhjNDJiM2RmN2I4MGRiMzY3NTNiOTE0ZGUwMzhjZGE5ZWE1Y2EyZTk2
11
+ Y2JmMmY3YzUyZDZkODYxYzVlMzhkNTg3ZThiMDIzY2ZlNzNhYzU=
12
+ data.tar.gz: !binary |-
13
+ ZTZhZWExODIxYWQzNTlkNDJkMzg0OWI4NWVlOTMxYzZlNzk1ZTQzZmZlYjEz
14
+ NGRhM2Q1MjIxYjQ4N2M5NWRiMjc4YmNmMjQ5MWNiYTEwZWU3NGYwYjhjNzkx
15
+ OTc0YzFiODY3NWNkZTMyOWJiODVhYzQyZGJkYjRlYzAwMGFiMjk=
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.idea/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private -m markdown lib/**/*.*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vorpal.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Sean Kirby
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,167 @@
1
+ # Vorpal
2
+
3
+ Separate your domain model from your delivery mechanism.
4
+
5
+ ## Overview
6
+ Vorpal is a [Data Mapper](http://martinfowler.com/eaaCatalog/dataMapper.html)-style ORM (object relational mapper) framelet that persists POROs (plain old Ruby objects) to a relational DB.
7
+
8
+ We say 'framelet' because it doesn't attempt to give you all the goodies that ORMs usually provide. Instead, it layers on top of an existing ORM and allows you to use the simplicity of the Active Record pattern where appropriate and the power of the Data Mapper pattern when you need it.
9
+
10
+ 3 things set it apart from existing Ruby ORMs (ActiveRecord and Datamapper):
11
+
12
+ 1. It keeps persistence concerns separate from domain logic. In other words, your domain models don't have to extend ActiveRecord::Base (or something else) in order to get saved to a DB.
13
+ 1. It works with [Aggregates](http://martinfowler.com/bliki/DDD_Aggregate.html) rather than individual objects.
14
+ 1. It plays nicely with ActiveRecord objects!
15
+
16
+ [Perpetuity](https://github.com/jgaskins/perpetuity) has a great introduction.
17
+
18
+ Talk about EDR? Victor has a good explanation of why domain model and delivery mechanism should be separated.
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'vorpal'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install vorpal
35
+
36
+ ## Usage
37
+ Start with a domain model of POROs that form an aggregate:
38
+
39
+ ```ruby
40
+ class Tree; end
41
+
42
+ class Trunk
43
+ include Virtus.model
44
+
45
+ attribute :id, Integer
46
+ attribute :length, Decimal
47
+ attribute :tree, Tree
48
+ end
49
+
50
+ class Branch
51
+ include Virtus.model
52
+
53
+ attribute :id, Integer
54
+ attribute :length, Decimal
55
+ attribute :tree, Tree
56
+ end
57
+
58
+ class Tree
59
+ include Virtus.model
60
+
61
+ attribute :id, Integer
62
+ attribute :name, String
63
+ attribute :trunk, Trunk
64
+ attribute :branches, Array[Branch]
65
+ end
66
+ ```
67
+
68
+ Along with a relational model:
69
+
70
+ ```sql
71
+ CREATE TABLE trees
72
+ (
73
+ id serial NOT NULL,
74
+ name text,
75
+ trunk_id integer
76
+ )
77
+
78
+ CREATE TABLE trunks
79
+ (
80
+ id serial NOT NULL,
81
+ length numeric
82
+ )
83
+
84
+ CREATE TABLE branches
85
+ (
86
+ id serial NOT NULL,
87
+ length numeric,
88
+ tree_id integer
89
+ )
90
+ ```
91
+
92
+ Create a repository configured to persist the aggregate to the relational model:
93
+
94
+ ```ruby
95
+ repository = Persistence::Configuration.define do
96
+ map Tree do
97
+ fields :name
98
+ belongs_to :trunk
99
+ has_many :branches
100
+ end
101
+
102
+ map Trunk do
103
+ fields :length
104
+ has_one :tree
105
+ end
106
+
107
+ map Branch do
108
+ fields :length
109
+ belongs_to :tree
110
+ end
111
+ end
112
+ ```
113
+ Why don't we use DDD language? I see no mention of aggregates and entities!
114
+
115
+ And use it:
116
+
117
+ ```ruby
118
+ repository.persist(big_tree)
119
+
120
+ small_tree = repository.load(small_tree_id, Tree)
121
+
122
+ repository.destroy(dead_tree)
123
+ ```
124
+
125
+ Show implementation of a repository using the aggregate repository!!!
126
+
127
+ Talk about aggregate boundary.
128
+
129
+ ### With ActiveRecord
130
+ TBD
131
+
132
+ ## API Documentation
133
+
134
+ (http://rubydoc.info/github/nulogy/vorpal/master/frames)
135
+
136
+ ## Caveats
137
+ It also does not do some things that you might expect from other ORMs:
138
+
139
+ 1. There is no lazy loading of associations. This might sound like a big deal, but with [correctly designed aggregates](http://dddcommunity.org/library/vernon_2011/) it turns out to be very minor.
140
+ 1. There is no managing of transactions. It is the strong opinion of the authors that managing transactions is an application-level concern.
141
+ 1. No support for validations. Validations are not a persistence concern.
142
+ 1. Only supports primary keys called `id`.
143
+ 1. Only supports PostgreSQL.
144
+ 1. Requires domain entities to have a special implementation of `#initialize`.
145
+ 1. Has a dependency on ActiveRecord.
146
+ 1. No facilities for querying the DB.
147
+ 1. Identity map only applies to a single `#load` or `#load_all` call.
148
+ 1. Clients cannot specify primary key values.
149
+
150
+ ## Future Enhancements
151
+ * Aggregate updated_at.
152
+ * Support for other DBMSs.
153
+ * Identity map for an entire application transaction.
154
+ * Value objects.
155
+ * Remove dependency on ActiveRecord (optimistic locking? connection pooling?)
156
+ * Application-generated primary key ids.
157
+ * More efficient object loading (use fewer queries.)
158
+ * Do not require special `#initialize` method? Provide a hook for an instance factory?
159
+ * Single table inheritance?
160
+
161
+ ## Contributing
162
+
163
+ 1. Fork it ( https://github.com/nulogy/vorpal/fork )
164
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
165
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
166
+ 4. Push to the branch (`git push origin my-new-feature`)
167
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,67 @@
1
+ # Simple framelet for deserialization
2
+ #
3
+ # class SomeDeserializer < SimpleDeserializer
4
+ # data_attributes :site_id, :name, :category_id, :integration_key
5
+ #
6
+ # def integration_key(old_integration_key)
7
+ # "XX#{@data[:other_attr]}XX#{old_integration_key}XX"
8
+ # end
9
+ #
10
+ # def set_category_id(category_id)
11
+ # object.category = InventoryStatusCategory.from_id(category_id)
12
+ # end
13
+ # end
14
+ #
15
+ # Usage:
16
+ #
17
+ # SomeDeserializer.deserialize(object, data)
18
+ # SomeDeserializer.new(object, data).deserialize
19
+ #
20
+ # SomeDeserializer.deserialize_array([object1, object2, ...], [data1, data2, ...])
21
+ #
22
+ class SimpleDeserializer
23
+ class << self
24
+ attr_accessor :_attributes
25
+
26
+ def inherited(base)
27
+ base._attributes = []
28
+ end
29
+
30
+ def data_attributes(*attrs)
31
+ @_attributes.concat attrs
32
+
33
+ attrs.each do |attr|
34
+ define_method attr do |datum|
35
+ datum
36
+ end unless method_defined?(attr)
37
+
38
+ define_method "set_#{attr}" do |datum|
39
+ object.send("#{attr}=", send(attr, datum))
40
+ end unless method_defined?("set_#{attr}")
41
+ end
42
+ end
43
+
44
+ def deserialize_array(objects, data)
45
+ objects.zip(data).map { |obj, datum| deserialize(obj, datum) }
46
+ end
47
+
48
+ def deserialize(object, data)
49
+ self.new(object, data).deserialize
50
+ end
51
+ end
52
+
53
+ attr_accessor :object
54
+
55
+ def initialize(object, data)
56
+ @object = object
57
+ @data = data
58
+ end
59
+
60
+ def deserialize
61
+ self.class._attributes.dup.each do |name|
62
+ next unless @data.has_key?(name)
63
+ send("set_#{name}", @data[name])
64
+ end
65
+ object
66
+ end
67
+ end
@@ -0,0 +1,72 @@
1
+ # Simple framelet for serialization.
2
+ #
3
+ # API compatible with ActiveModel::Serializer but without all the complexity
4
+ # and dependence on ActiveModel
5
+ #
6
+ # class SomeSerializer < SimpleSerializer
7
+ # attributes :id, :name, :category_id, :errors
8
+ #
9
+ # def category_id
10
+ # object.category.try(:id)
11
+ # end
12
+ #
13
+ # def errors
14
+ # ActiveModelErrorsSerializer.serialize_errors(object.errors, true) if object.errors.any?
15
+ # end
16
+ # end
17
+ #
18
+ # Usage:
19
+ #
20
+ # SomeSerializer.serialize(object)
21
+ # SomeSerializer.new(object).serialize
22
+ #
23
+ # SomeSerializer.serialize_array([object])
24
+ #
25
+ class SimpleSerializer
26
+ class << self
27
+ attr_accessor :_attributes
28
+
29
+ def inherited(base)
30
+ base._attributes = []
31
+ end
32
+
33
+ def attributes(*attrs)
34
+ @_attributes.concat attrs
35
+
36
+ attrs.each do |attr|
37
+ define_method attr do
38
+ object.send(attr)
39
+ end unless method_defined?(attr)
40
+ end
41
+ end
42
+
43
+ def serialize_array(objects)
44
+ objects.map { |obj| serialize(obj) }
45
+ end
46
+
47
+ def serialize(object)
48
+ self.new(object).serialize
49
+ end
50
+
51
+ alias :as_json :serialize
52
+ end
53
+
54
+ attr_accessor :object
55
+
56
+ def initialize(object, _={})
57
+ @object = object
58
+ end
59
+
60
+ def attributes
61
+ self.class._attributes.dup.each_with_object({}) do |name, hash|
62
+ hash[name] = send(name)
63
+ end
64
+ end
65
+
66
+ def serialize(_={})
67
+ return nil if object.nil?
68
+ attributes
69
+ end
70
+ alias :as_json :serialize
71
+ end
72
+