diametric 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +14 -3
- data/README.md +26 -1
- data/Rakefile +17 -1
- data/diametric.gemspec +8 -7
- data/lib/diametric.rb +8 -0
- data/lib/diametric/config.rb +54 -0
- data/lib/diametric/config/environment.rb +42 -0
- data/lib/diametric/entity.rb +126 -28
- data/lib/diametric/generators/active_model.rb +42 -0
- data/lib/diametric/persistence.rb +49 -0
- data/lib/diametric/persistence/common.rb +21 -0
- data/lib/diametric/persistence/peer.rb +17 -11
- data/lib/diametric/persistence/rest.rb +14 -6
- data/lib/diametric/query.rb +34 -3
- data/lib/diametric/railtie.rb +52 -0
- data/lib/diametric/version.rb +1 -1
- data/lib/tasks/create_schema.rb +27 -0
- data/lib/tasks/diametric_config.rb +45 -0
- data/spec/diametric/config_spec.rb +60 -0
- data/spec/diametric/entity_spec.rb +96 -27
- data/spec/diametric/persistence/peer_spec.rb +2 -2
- data/spec/diametric/persistence/rest_spec.rb +10 -3
- data/spec/diametric/persistence_spec.rb +59 -0
- data/spec/diametric/query_spec.rb +56 -0
- data/spec/integration_spec.rb +29 -1
- data/spec/peer_integration_spec.rb +37 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/gen_entity_class.rb +7 -0
- data/spec/support/persistence_examples.rb +33 -1
- metadata +36 -13
- data/.gitignore +0 -22
- data/Guardfile +0 -8
- data/TODO.org +0 -12
data/Gemfile
CHANGED
@@ -5,10 +5,21 @@ gemspec
|
|
5
5
|
|
6
6
|
# Development-only dependencies
|
7
7
|
gem 'rake'
|
8
|
-
gem 'pry'
|
9
8
|
gem 'rspec'
|
9
|
+
gem 'pry'
|
10
|
+
|
11
|
+
gem 'guard'
|
12
|
+
gem 'guard-rspec'
|
13
|
+
gem 'rb-inotify', :require => false
|
14
|
+
gem 'rb-fsevent', :require => false
|
15
|
+
gem 'rb-fchange', :require => false
|
10
16
|
|
11
17
|
platform :mri do
|
12
|
-
gem 'yard'
|
13
|
-
gem 'redcarpet'
|
18
|
+
gem 'yard', :group => :development
|
19
|
+
gem 'redcarpet', :group => :development
|
20
|
+
end
|
21
|
+
|
22
|
+
platform :jruby do
|
23
|
+
gem 'jruby-openssl'
|
24
|
+
gem 'lock_jar'
|
14
25
|
end
|
data/README.md
CHANGED
@@ -1,9 +1,19 @@
|
|
1
|
+
[![Build Status](https://secure.travis-ci.org/relevance/diametric.png)](http://travis-ci.org/relevance/diametric)
|
2
|
+
|
1
3
|
# Diametric
|
2
4
|
|
3
5
|
Diametric is a library for building schemas, queries, and transactions
|
4
6
|
for [Datomic][] from Ruby objects. It is also used to map Ruby objects
|
5
7
|
as entities into a Datomic database.
|
6
8
|
|
9
|
+
## Thanks
|
10
|
+
|
11
|
+
Development of Diametric was sponsored by [Relevance][]. They are the
|
12
|
+
best Clojure shop around and one of the best Ruby shops. I highly
|
13
|
+
recommend them for help with your corporate projects.
|
14
|
+
|
15
|
+
Special thanks to Mongoid for writing some solid ORM code that was liberally borrowed from to add Rails support to Diametric.
|
16
|
+
|
7
17
|
## Entity API
|
8
18
|
|
9
19
|
The `Entity` module is interesting, in that it is primarily made of
|
@@ -150,7 +160,7 @@ require 'diametric/persistence/rest'
|
|
150
160
|
|
151
161
|
# database url, database alias, database name
|
152
162
|
# will create database if it does not already exist
|
153
|
-
Diametric::Persistence::REST.connect('http://localhost:9000', '
|
163
|
+
Diametric::Persistence::REST.connect('http://localhost:9000', 'free', 'animals')
|
154
164
|
```
|
155
165
|
|
156
166
|
### Using persisted models
|
@@ -212,4 +222,19 @@ Or install it yourself as:
|
|
212
222
|
4. Push to the branch (`git push origin my-new-feature`)
|
213
223
|
5. Create new Pull Request
|
214
224
|
|
225
|
+
## License
|
226
|
+
|
227
|
+
This project uses the [BSD License][].
|
228
|
+
|
229
|
+
Copyright (c) 2012, Clinton Dreisbach & Relevance Inc. All rights reserved.
|
230
|
+
|
231
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
232
|
+
|
233
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
234
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
235
|
+
|
236
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
237
|
+
|
215
238
|
[Datomic]: http://www.datomic.com
|
239
|
+
[Relevance]: http://www.thinkrelevance.com
|
240
|
+
[BSD License]: http://opensource.org/licenses/BSD-2-Clause
|
data/Rakefile
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
begin
|
2
2
|
require "bundler/gem_tasks"
|
3
|
+
require 'rspec/core/rake_task'
|
3
4
|
rescue LoadError
|
4
5
|
end
|
5
6
|
|
7
|
+
|
6
8
|
task :default => :prepare
|
7
9
|
|
8
|
-
task :prepare do
|
10
|
+
task :prepare => :install_lockjar do
|
9
11
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
10
12
|
require 'lock_jar'
|
11
13
|
|
@@ -15,3 +17,17 @@ task :prepare do
|
|
15
17
|
LockJar.install(lockfile)
|
16
18
|
end
|
17
19
|
end
|
20
|
+
|
21
|
+
task :install_lockjar do
|
22
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
23
|
+
require 'rubygems'
|
24
|
+
require 'rubygems/dependency_installer'
|
25
|
+
inst = Gem::DependencyInstaller.new
|
26
|
+
inst.install 'lock_jar', '~> 0.7.2'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Run all RSpec tests"
|
31
|
+
require 'rspec'
|
32
|
+
require 'rspec/core/rake_task'
|
33
|
+
RSpec::Core::RakeTask.new(:spec)
|
data/diametric.gemspec
CHANGED
@@ -6,26 +6,27 @@ require 'diametric/version'
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "diametric"
|
8
8
|
gem.version = Diametric::VERSION
|
9
|
-
gem.authors = ["Clinton N. Dreisbach"]
|
10
|
-
gem.email = ["crnixon@gmail.com"]
|
9
|
+
gem.authors = ["Clinton N. Dreisbach", "Ryan K. Neufeld", "Yoko Harada"]
|
10
|
+
gem.email = ["crnixon@gmail.com", "ryan@thinkrelevance.com", "yoko@thinkrelevance.com"]
|
11
11
|
gem.summary = %q{ActiveModel for Datomic}
|
12
12
|
gem.description = <<EOF
|
13
13
|
Diametric is a library for building schemas, queries, and transactions
|
14
14
|
for Datomic from Ruby objects. It is also used to map Ruby objects
|
15
15
|
as entities into a Datomic database.
|
16
16
|
EOF
|
17
|
-
gem.homepage = "https://github.com/
|
17
|
+
gem.homepage = "https://github.com/relevance/diametric"
|
18
18
|
|
19
|
-
gem.files =
|
20
|
-
gem.executables =
|
21
|
-
gem.test_files =
|
19
|
+
gem.files = %w(Gemfile Jarfile Jarfile.lock LICENSE.txt README.md Rakefile diametric.gemspec) + Dir.glob('lib/**/*')
|
20
|
+
gem.executables = []
|
21
|
+
gem.test_files = Dir.glob("spec/**/*.rb")
|
22
22
|
gem.require_paths = ["lib"]
|
23
23
|
|
24
24
|
gem.add_dependency 'edn', '~> 1.0'
|
25
25
|
gem.add_dependency 'activesupport', '>= 3.0.0'
|
26
26
|
gem.add_dependency 'activemodel', '>= 3.0.0'
|
27
27
|
gem.add_dependency 'datomic-client', '~> 0.4.1'
|
28
|
-
gem.add_dependency '
|
28
|
+
gem.add_dependency 'rspec'
|
29
|
+
gem.add_dependency 'lock_jar', '= 0.7.2' if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
29
30
|
|
30
31
|
gem.extensions = ['Rakefile']
|
31
32
|
end
|
data/lib/diametric.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
require "diametric/version"
|
2
2
|
require "diametric/entity"
|
3
3
|
require "diametric/query"
|
4
|
+
require "diametric/persistence"
|
5
|
+
|
6
|
+
require 'diametric/config'
|
7
|
+
|
8
|
+
if defined?(Rails)
|
9
|
+
require 'diametric/railtie'
|
10
|
+
end
|
4
11
|
|
5
12
|
# Diametric is a library for building schemas, queries, and
|
6
13
|
# transactions for Datomic from Ruby objects. It is also used to map
|
7
14
|
# Ruby objects as entities into a Datomic database.
|
8
15
|
module Diametric
|
16
|
+
extend Diametric::Config
|
9
17
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'diametric/config/environment'
|
2
|
+
require 'diametric/persistence'
|
3
|
+
|
4
|
+
module Diametric
|
5
|
+
# Program-level configuration services including configuration loading and base connections.
|
6
|
+
module Config
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# The current configuration Diametric will use in {#connect!}
|
10
|
+
#
|
11
|
+
# @return [Hash]
|
12
|
+
def configuration
|
13
|
+
@configuration ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Determine if Diametric has been configured
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
19
|
+
def configured?
|
20
|
+
configuration.present?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Load settings from a compliant diametric.yml file and make a connection. This can be used for
|
24
|
+
# easy setup with frameworks other than Rails.
|
25
|
+
#
|
26
|
+
# See {Persistence} for valid options.
|
27
|
+
#
|
28
|
+
# @example Configure Diametric.
|
29
|
+
# Diametric.load_and_connect!("/path/to/diametric.yml")
|
30
|
+
#
|
31
|
+
# @param [ String ] path The path to the file.
|
32
|
+
# @param [ String, Symbol ] environment The environment to load.
|
33
|
+
def load_and_connect!(path, environment = nil)
|
34
|
+
settings = Environment.load_yaml(path, environment)
|
35
|
+
@configuration = settings.with_indifferent_access
|
36
|
+
connect!(configuration)
|
37
|
+
peer_setup if Diametric::Persistence.peer?
|
38
|
+
configuration
|
39
|
+
end
|
40
|
+
|
41
|
+
# Establish a base connection from the supplied configuration hash.
|
42
|
+
#
|
43
|
+
# @param [ Hash ] configuration The configuration of the database to connect to. See {Persistence.establish_base_connection} for valid options.
|
44
|
+
def connect!(configuration)
|
45
|
+
::Diametric::Persistence.establish_base_connection(configuration)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def peer_setup
|
50
|
+
load File.join(File.dirname(__FILE__), '..', 'tasks', 'create_schema.rb')
|
51
|
+
Rake::Task["diametric:create_schema_for_peer"].invoke
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Diametric
|
2
|
+
module Config
|
3
|
+
|
4
|
+
# Encapsulates logic for getting environment information.
|
5
|
+
#
|
6
|
+
# This is nearly a word-for-word copy of {http://github.com/mongoid/mongoid Mongoid}'s {https://github.com/mongoid/mongoid/blob/master/lib/mongoid/config/environment.rb lib/mongoid/config/environment.rb} file.
|
7
|
+
# Thanks!
|
8
|
+
module Environment
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Get the name of the environment that we are running under. This first
|
12
|
+
# looks for Rails, then Sinatra, then a RACK_ENV environment variable,
|
13
|
+
# and if none of those are found raises an error.
|
14
|
+
#
|
15
|
+
# @example Get the env name.
|
16
|
+
# Environment.env_name
|
17
|
+
#
|
18
|
+
# @raise [ "No environment specified" ] If no environment was set.
|
19
|
+
#
|
20
|
+
# @return [ String ] The name of the current environment.
|
21
|
+
def env_name
|
22
|
+
return Rails.env if defined?(Rails)
|
23
|
+
return Sinatra::Base.environment.to_s if defined?(Sinatra)
|
24
|
+
ENV["RACK_ENV"] || ENV["DIAMETRIC_ENV"] || raise("No environment specified")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Load the yaml from the provided path and return the settings for the
|
28
|
+
# current environment.
|
29
|
+
#
|
30
|
+
# @example Load the yaml.
|
31
|
+
# Environment.load_yaml("/work/diametric.yml")
|
32
|
+
#
|
33
|
+
# @param [ String ] path The location of the file.
|
34
|
+
#
|
35
|
+
# @return [ Hash ] The settings.
|
36
|
+
def load_yaml(path, environment = nil)
|
37
|
+
env = environment ? environment.to_s : env_name
|
38
|
+
YAML.load(ERB.new(File.new(path).read).result)[env]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/diametric/entity.rb
CHANGED
@@ -28,6 +28,7 @@ module Diametric
|
|
28
28
|
# The database id assigned to the entity by Datomic.
|
29
29
|
# @return [Integer]
|
30
30
|
module Entity
|
31
|
+
Ref = "ref"
|
31
32
|
# Conversions from Ruby types to Datomic types.
|
32
33
|
VALUE_TYPES = {
|
33
34
|
Symbol => "keyword",
|
@@ -39,6 +40,10 @@ module Diametric
|
|
39
40
|
URI => "uri"
|
40
41
|
}
|
41
42
|
|
43
|
+
DEFAULT_OPTIONS = {
|
44
|
+
:cardinality => :one
|
45
|
+
}
|
46
|
+
|
42
47
|
@temp_ref = -1000
|
43
48
|
|
44
49
|
def self.included(base)
|
@@ -46,9 +51,12 @@ module Diametric
|
|
46
51
|
base.send(:extend, ActiveModel::Naming)
|
47
52
|
base.send(:include, ActiveModel::Conversion)
|
48
53
|
base.send(:include, ActiveModel::Dirty)
|
54
|
+
base.send(:include, ActiveModel::Validations)
|
49
55
|
|
50
56
|
base.class_eval do
|
51
|
-
@attributes =
|
57
|
+
@attributes = {}
|
58
|
+
@defaults = {}
|
59
|
+
@namespace_prefix = nil
|
52
60
|
@partition = :"db.part/user"
|
53
61
|
end
|
54
62
|
end
|
@@ -64,6 +72,9 @@ module Diametric
|
|
64
72
|
#
|
65
73
|
# @!attribute [r] attributes
|
66
74
|
# @return [Array] Definitions of each of the entity's attributes (name, type, options).
|
75
|
+
#
|
76
|
+
# @!attribute [r] defaults
|
77
|
+
# @return [Array] Default values for any entitites defined with a +:default+ key and value.
|
67
78
|
module ClassMethods
|
68
79
|
def partition
|
69
80
|
@partition
|
@@ -77,13 +88,35 @@ module Diametric
|
|
77
88
|
@attributes
|
78
89
|
end
|
79
90
|
|
91
|
+
def defaults
|
92
|
+
@defaults
|
93
|
+
end
|
94
|
+
|
95
|
+
# Set the namespace prefix used for attribute names
|
96
|
+
#
|
97
|
+
# @param prefix [#to_s] The prefix to be used for namespacing entity attributes
|
98
|
+
#
|
99
|
+
# @example Override the default namespace prefix
|
100
|
+
# class Mouse
|
101
|
+
# include Diametric::Entity
|
102
|
+
#
|
103
|
+
# namespace_prefix :mice
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# Mouse.new.prefix # => :mice
|
107
|
+
#
|
108
|
+
# @return void
|
109
|
+
def namespace_prefix(prefix)
|
110
|
+
@namespace_prefix = prefix
|
111
|
+
end
|
112
|
+
|
80
113
|
# Add an attribute to a {Diametric::Entity}.
|
81
114
|
#
|
82
115
|
# Valid options are:
|
83
116
|
#
|
84
117
|
# * +:index+: The only valid value is +true+. This causes the
|
85
118
|
# attribute to be indexed for easier lookup.
|
86
|
-
# * +:unique+: Valid values are +:value+ or +:identity
|
119
|
+
# * +:unique+: Valid values are +:value+ or +:identity.+
|
87
120
|
# * +:value+ causes the attribute value to be unique to the
|
88
121
|
# entity and attempts to insert a duplicate value will fail.
|
89
122
|
# * +:identity+ causes the attribute value to be unique to
|
@@ -98,11 +131,13 @@ module Diametric
|
|
98
131
|
# single value with an entity.
|
99
132
|
# * +:many+ - the attribute is mutli valued, it associates a
|
100
133
|
# set of values with an entity.
|
101
|
-
# To be honest, I have no idea how this will work in Ruby. Try +:many+ at your own risk.
|
102
134
|
# +:one+ is the default.
|
103
135
|
# * +:doc+: A string used in Datomic to document the attribute.
|
104
136
|
# * +:fulltext+: The only valid value is +true+. Indicates that a
|
105
137
|
# fulltext search index should be generated for the attribute.
|
138
|
+
# * +:default+: The value the attribute will default to when the
|
139
|
+
# Entity is initialized. Defaults for attributes with +:cardinality+ of +:many+
|
140
|
+
# will be transformed into a Set by passing the default to +Set.new+.
|
106
141
|
#
|
107
142
|
# @example Add an indexed name attribute.
|
108
143
|
# attribute :name, String, :index => true
|
@@ -114,20 +149,18 @@ module Diametric
|
|
114
149
|
#
|
115
150
|
# @return void
|
116
151
|
def attribute(name, value_type, opts = {})
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
instance_variable_set("@#{name}", value)
|
125
|
-
end
|
152
|
+
opts = DEFAULT_OPTIONS.merge(opts)
|
153
|
+
|
154
|
+
establish_defaults(name, value_type, opts)
|
155
|
+
|
156
|
+
@attributes[name] = {:value_type => value_type}.merge(opts)
|
157
|
+
|
158
|
+
setup_attribute_methods(name, opts[:cardinality])
|
126
159
|
end
|
127
160
|
|
128
161
|
# @return [Array<Symbol>] Names of the entity's attributes.
|
129
162
|
def attribute_names
|
130
|
-
@attributes.
|
163
|
+
@attributes.keys
|
131
164
|
end
|
132
165
|
|
133
166
|
# Generates a Datomic schema for a model's attributes.
|
@@ -141,10 +174,12 @@ module Diametric
|
|
141
174
|
:"db.install/_attribute" => :"db.part/db"
|
142
175
|
}
|
143
176
|
|
144
|
-
@attributes.reduce([]) do |schema, (attribute,
|
177
|
+
@attributes.reduce([]) do |schema, (attribute, opts)|
|
145
178
|
opts = opts.dup
|
179
|
+
value_type = opts.delete(:value_type)
|
180
|
+
|
146
181
|
unless opts.empty?
|
147
|
-
opts[:cardinality] = namespace("db.cardinality", opts[:cardinality])
|
182
|
+
opts[:cardinality] = namespace("db.cardinality", opts[:cardinality])
|
148
183
|
opts[:unique] = namespace("db.unique", opts[:unique]) if opts[:unique]
|
149
184
|
opts = opts.map { |k, v|
|
150
185
|
k = namespace("db", k)
|
@@ -166,12 +201,13 @@ module Diametric
|
|
166
201
|
# @return [Entity]
|
167
202
|
def from_query(query_results)
|
168
203
|
dbid = query_results.shift
|
169
|
-
widget = self.new(Hash[
|
204
|
+
widget = self.new(Hash[attribute_names.zip query_results])
|
170
205
|
widget.dbid = dbid
|
171
206
|
widget
|
172
207
|
end
|
173
208
|
|
174
|
-
# Returns the prefix for this model used in Datomic.
|
209
|
+
# Returns the prefix for this model used in Datomic. Can be
|
210
|
+
# overriden by declaring {#namespace_prefix}
|
175
211
|
#
|
176
212
|
# @example
|
177
213
|
# Mouse.prefix #=> "mouse"
|
@@ -180,7 +216,7 @@ module Diametric
|
|
180
216
|
#
|
181
217
|
# @return [String]
|
182
218
|
def prefix
|
183
|
-
self.to_s.underscore.sub('/', '.')
|
219
|
+
@namespace_prefix || self.to_s.underscore.sub('/', '.')
|
184
220
|
end
|
185
221
|
|
186
222
|
# Create a temporary id placeholder.
|
@@ -212,6 +248,27 @@ module Diametric
|
|
212
248
|
end
|
213
249
|
namespace("db.type", vt)
|
214
250
|
end
|
251
|
+
|
252
|
+
def establish_defaults(name, value_type, opts = {})
|
253
|
+
default = opts.delete(:default)
|
254
|
+
@defaults[name] = default if default
|
255
|
+
end
|
256
|
+
|
257
|
+
def setup_attribute_methods(name, cardinality)
|
258
|
+
define_attribute_method name
|
259
|
+
|
260
|
+
define_method(name) do
|
261
|
+
instance_variable_get("@#{name}")
|
262
|
+
end
|
263
|
+
|
264
|
+
define_method("#{name}=") do |value|
|
265
|
+
send("#{name}_will_change!") unless value == instance_variable_get("@#{name}")
|
266
|
+
if cardinality == :many
|
267
|
+
value = Set.new(value)
|
268
|
+
end
|
269
|
+
instance_variable_set("@#{name}", value)
|
270
|
+
end
|
271
|
+
end
|
215
272
|
end
|
216
273
|
|
217
274
|
def dbid
|
@@ -230,7 +287,7 @@ module Diametric
|
|
230
287
|
# @param params [Hash] A hash of attributes and values to
|
231
288
|
# initialize the entity with.
|
232
289
|
def initialize(params = {})
|
233
|
-
params.each do |k, v|
|
290
|
+
self.class.defaults.merge(params).each do |k, v|
|
234
291
|
self.send("#{k}=", v)
|
235
292
|
end
|
236
293
|
end
|
@@ -243,19 +300,45 @@ module Diametric
|
|
243
300
|
|
244
301
|
# Creates data for a Datomic transaction.
|
245
302
|
#
|
246
|
-
# @param
|
247
|
-
# transaction. If no
|
303
|
+
# @param attribute_names [*Symbol] Attribute names to save in the
|
304
|
+
# transaction. If no names are given, any changed
|
248
305
|
# attributes will be saved.
|
249
306
|
#
|
250
307
|
# @return [Array] Datomic transaction data.
|
251
|
-
def tx_data(*
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
308
|
+
def tx_data(*attribute_names)
|
309
|
+
attribute_names = self.changed_attributes.keys if attribute_names.empty?
|
310
|
+
|
311
|
+
entity_tx = {}
|
312
|
+
txes = []
|
313
|
+
attribute_names.each do |attribute_name|
|
314
|
+
cardinality = self.class.attributes[attribute_name.to_sym][:cardinality]
|
315
|
+
|
316
|
+
if cardinality == :many
|
317
|
+
txes += cardinality_many_tx_data(attribute_name)
|
318
|
+
else
|
319
|
+
entity_tx[self.class.namespace(self.class.prefix, attribute_name)] = self.send(attribute_name)
|
320
|
+
end
|
257
321
|
end
|
258
|
-
|
322
|
+
|
323
|
+
if entity_tx.present?
|
324
|
+
txes << entity_tx.merge({:"db/id" => dbid || tempid})
|
325
|
+
end
|
326
|
+
|
327
|
+
txes
|
328
|
+
end
|
329
|
+
|
330
|
+
def cardinality_many_tx_data(attribute_name)
|
331
|
+
prev = Array(self.changed_attributes[attribute_name]).to_set
|
332
|
+
curr = self.send(attribute_name)
|
333
|
+
|
334
|
+
protractions = curr - prev
|
335
|
+
retractions = prev - curr
|
336
|
+
|
337
|
+
txes = []
|
338
|
+
namespaced_attribute = self.class.namespace(self.class.prefix, attribute_name)
|
339
|
+
txes << [:"db/retract", (dbid || tempid), namespaced_attribute, retractions.to_a] unless retractions.empty?
|
340
|
+
txes << [:"db/add", (dbid || tempid) , namespaced_attribute, protractions.to_a] unless protractions.empty?
|
341
|
+
txes
|
259
342
|
end
|
260
343
|
|
261
344
|
# @return [Array<Symbol>] Names of the entity's attributes.
|
@@ -335,5 +418,20 @@ module Diametric
|
|
335
418
|
def errors
|
336
419
|
@errors ||= ActiveModel::Errors.new(self)
|
337
420
|
end
|
421
|
+
|
422
|
+
# Update method updates attribute values and saves new values in datomic.
|
423
|
+
# The method receives hash as an argument.
|
424
|
+
def update(attrs)
|
425
|
+
attrs.each do |k, v|
|
426
|
+
self.send(k.to_s+"=", v)
|
427
|
+
self.changed_attributes[k]=v
|
428
|
+
end
|
429
|
+
self.save if self.respond_to? :save
|
430
|
+
true
|
431
|
+
end
|
432
|
+
|
433
|
+
def destroy
|
434
|
+
self.retract_entity(dbid)
|
435
|
+
end
|
338
436
|
end
|
339
437
|
end
|