modis 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.rubocop.yml +24 -0
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/Gemfile +1 -0
- data/Gemfile.lock +95 -0
- data/Rakefile +5 -3
- data/lib/modis/attribute.rb +82 -0
- data/lib/modis/errors.rb +2 -0
- data/lib/modis/finder.rb +64 -0
- data/lib/modis/index.rb +92 -0
- data/lib/modis/model.rb +9 -11
- data/lib/modis/persistence.rb +79 -41
- data/lib/modis/version.rb +1 -1
- data/lib/modis.rb +5 -4
- data/lib/tasks/quality.rake +34 -0
- data/spec/attribute_spec.rb +153 -0
- data/spec/errors_spec.rb +1 -1
- data/spec/{finders_spec.rb → finder_spec.rb} +18 -19
- data/spec/index_spec.rb +84 -0
- data/spec/persistence_spec.rb +37 -37
- data/spec/spec_helper.rb +1 -1
- data/spec/support/simplecov_quality_formatter.rb +9 -5
- data/spec/transaction_spec.rb +2 -2
- data/spec/validations_spec.rb +5 -5
- metadata +14 -9
- data/lib/modis/attributes.rb +0 -138
- data/lib/modis/finders.rb +0 -48
- data/lib/tasks/spec.rake +0 -18
- data/spec/attributes_spec.rb +0 -182
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c1496e1e5c9b430c4aa6257d4ec122d3cb1ee37
|
4
|
+
data.tar.gz: 68c023c5b84245530772181c7a2bf564ebe0712f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f77b3079fd2c210bb678b952710e188dda3eb1ae8d98d00f30b79b511671305df22a3d5863433c48c2a831bf795580afc8a6c1d6973530a0847e7a5f14cabfad
|
7
|
+
data.tar.gz: d22b018df72ac21e0bce75fba13816ecf7067b96602afae6f0d8d09321d2f24c8c194809d30bc221e543bcbc5bd4330157248eed20ac761c1560d0d647b99ddc
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- modis.gemspec
|
4
|
+
|
5
|
+
LineLength:
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
StringLiterals:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Documentation:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
MethodLength:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
ClassLength:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
CyclomaticComplexity:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Style/SignalException:
|
24
|
+
EnforcedStyle: only_raise
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.1.
|
1
|
+
ruby-2.1.2
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
modis (1.2.0)
|
5
|
+
activemodel (>= 3.0)
|
6
|
+
activesupport (>= 3.0)
|
7
|
+
connection_pool (>= 2)
|
8
|
+
redis (>= 3.0)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activemodel (4.1.5)
|
14
|
+
activesupport (= 4.1.5)
|
15
|
+
builder (~> 3.1)
|
16
|
+
activesupport (4.1.5)
|
17
|
+
i18n (~> 0.6, >= 0.6.9)
|
18
|
+
json (~> 1.7, >= 1.7.7)
|
19
|
+
minitest (~> 5.1)
|
20
|
+
thread_safe (~> 0.1)
|
21
|
+
tzinfo (~> 1.1)
|
22
|
+
ast (2.0.0)
|
23
|
+
builder (3.2.2)
|
24
|
+
cane (2.6.2)
|
25
|
+
parallel
|
26
|
+
connection_pool (2.0.0)
|
27
|
+
coveralls (0.7.1)
|
28
|
+
multi_json (~> 1.3)
|
29
|
+
rest-client
|
30
|
+
simplecov (>= 0.7)
|
31
|
+
term-ansicolor
|
32
|
+
thor
|
33
|
+
diff-lcs (1.2.5)
|
34
|
+
docile (1.1.5)
|
35
|
+
i18n (0.6.11)
|
36
|
+
json (1.8.1)
|
37
|
+
mime-types (2.3)
|
38
|
+
minitest (5.4.1)
|
39
|
+
multi_json (1.10.1)
|
40
|
+
netrc (0.7.7)
|
41
|
+
parallel (1.3.0)
|
42
|
+
parser (2.2.0.pre.4)
|
43
|
+
ast (>= 1.1, < 3.0)
|
44
|
+
slop (~> 3.4, >= 3.4.5)
|
45
|
+
powerpack (0.0.9)
|
46
|
+
rainbow (2.0.0)
|
47
|
+
rake (10.3.2)
|
48
|
+
redis (3.1.0)
|
49
|
+
rest-client (1.7.2)
|
50
|
+
mime-types (>= 1.16, < 3.0)
|
51
|
+
netrc (~> 0.7)
|
52
|
+
rspec (3.0.0)
|
53
|
+
rspec-core (~> 3.0.0)
|
54
|
+
rspec-expectations (~> 3.0.0)
|
55
|
+
rspec-mocks (~> 3.0.0)
|
56
|
+
rspec-core (3.0.4)
|
57
|
+
rspec-support (~> 3.0.0)
|
58
|
+
rspec-expectations (3.0.4)
|
59
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
+
rspec-support (~> 3.0.0)
|
61
|
+
rspec-mocks (3.0.4)
|
62
|
+
rspec-support (~> 3.0.0)
|
63
|
+
rspec-support (3.0.4)
|
64
|
+
rubocop (0.25.0)
|
65
|
+
parser (>= 2.2.0.pre.4, < 3.0)
|
66
|
+
powerpack (~> 0.0.6)
|
67
|
+
rainbow (>= 1.99.1, < 3.0)
|
68
|
+
ruby-progressbar (~> 1.4)
|
69
|
+
ruby-progressbar (1.5.1)
|
70
|
+
simplecov (0.9.0)
|
71
|
+
docile (~> 1.1.0)
|
72
|
+
multi_json
|
73
|
+
simplecov-html (~> 0.8.0)
|
74
|
+
simplecov-html (0.8.0)
|
75
|
+
slop (3.6.0)
|
76
|
+
term-ansicolor (1.3.0)
|
77
|
+
tins (~> 1.0)
|
78
|
+
thor (0.19.1)
|
79
|
+
thread_safe (0.3.4)
|
80
|
+
tins (1.3.2)
|
81
|
+
tzinfo (1.2.2)
|
82
|
+
thread_safe (~> 0.1)
|
83
|
+
|
84
|
+
PLATFORMS
|
85
|
+
java
|
86
|
+
ruby
|
87
|
+
|
88
|
+
DEPENDENCIES
|
89
|
+
cane
|
90
|
+
coveralls
|
91
|
+
modis!
|
92
|
+
rake
|
93
|
+
rspec
|
94
|
+
rubocop
|
95
|
+
simplecov
|
data/Rakefile
CHANGED
@@ -8,8 +8,10 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
8
8
|
spec.rspec_opts = ['--backtrace']
|
9
9
|
end
|
10
10
|
|
11
|
-
if
|
12
|
-
task :
|
11
|
+
if ENV['TRAVIS'] && ENV['QUALITY'] == 'false'
|
12
|
+
task default: 'spec'
|
13
|
+
elsif RUBY_VERSION > '1.9' && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
14
|
+
task default: 'spec:quality'
|
13
15
|
else
|
14
|
-
task :
|
16
|
+
task default: 'spec'
|
15
17
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Modis
|
2
|
+
module Attribute
|
3
|
+
TYPES = [:string, :integer, :float, :timestamp, :boolean, :array, :hash]
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
base.instance_eval do
|
8
|
+
bootstrap_attributes
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def bootstrap_attributes
|
14
|
+
class << self
|
15
|
+
attr_accessor :attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
self.attributes = {}
|
19
|
+
|
20
|
+
attribute :id, :integer
|
21
|
+
end
|
22
|
+
|
23
|
+
def attribute(name, type = :string, options = {})
|
24
|
+
name = name.to_s
|
25
|
+
raise AttributeError, "Attribute with name '#{name}' has already been specified." if attributes.key?(name)
|
26
|
+
raise UnsupportedAttributeType, type unless TYPES.include?(type)
|
27
|
+
|
28
|
+
attributes[name] = options.update(type: type)
|
29
|
+
define_attribute_methods [name]
|
30
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
31
|
+
def #{name}
|
32
|
+
attributes['#{name}']
|
33
|
+
end
|
34
|
+
|
35
|
+
def #{name}=(value)
|
36
|
+
ensure_type('#{name}', value)
|
37
|
+
#{name}_will_change! unless value == attributes['#{name}']
|
38
|
+
attributes['#{name}'] = value
|
39
|
+
end
|
40
|
+
EOS
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def attributes
|
45
|
+
@attributes ||= Hash[self.class.attributes.keys.zip]
|
46
|
+
end
|
47
|
+
|
48
|
+
def assign_attributes(hash)
|
49
|
+
hash.each do |k, v|
|
50
|
+
setter = "#{k}="
|
51
|
+
send(setter, v) if respond_to?(setter)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_attribute(key, value)
|
56
|
+
attributes[key.to_s] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
def read_attribute(key)
|
60
|
+
attributes[key.to_s]
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def set_sti_type
|
66
|
+
return unless self.class.sti_child?
|
67
|
+
assign_attributes(type: self.class.name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def reset_changes
|
71
|
+
@changed_attributes.clear if @changed_attributes
|
72
|
+
end
|
73
|
+
|
74
|
+
def apply_defaults
|
75
|
+
defaults = {}
|
76
|
+
self.class.attributes.each do |attribute, options|
|
77
|
+
defaults[attribute] = options[:default] if options[:default]
|
78
|
+
end
|
79
|
+
assign_attributes(defaults)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/modis/errors.rb
CHANGED
data/lib/modis/finder.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Modis
|
2
|
+
module Finder
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def find(id)
|
9
|
+
Modis.with_connection do |redis|
|
10
|
+
attributes = attributes_for(redis, id)
|
11
|
+
model_for(attributes)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
records = []
|
17
|
+
|
18
|
+
Modis.with_connection do |redis|
|
19
|
+
ids = redis.smembers(key_for(:all))
|
20
|
+
records = redis.pipelined do
|
21
|
+
ids.map { |id| record_for(redis, id) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
records.map do |record|
|
26
|
+
attributes = record_to_attributes(record)
|
27
|
+
model_for(attributes)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def attributes_for(redis, id)
|
32
|
+
raise RecordNotFound, "Couldn't find #{name} without an ID" if id.nil?
|
33
|
+
|
34
|
+
attributes = record_to_attributes(record_for(redis, id))
|
35
|
+
|
36
|
+
unless attributes['id'].present?
|
37
|
+
raise RecordNotFound, "Couldn't find #{name} with id=#{id}"
|
38
|
+
end
|
39
|
+
|
40
|
+
attributes
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def model_for(attributes)
|
46
|
+
model_class(attributes).new(attributes, new_record: false)
|
47
|
+
end
|
48
|
+
|
49
|
+
def record_to_attributes(record)
|
50
|
+
record.each { |k, v| record[k] = coerce_from_persistence(v) }
|
51
|
+
record
|
52
|
+
end
|
53
|
+
|
54
|
+
def record_for(redis, id)
|
55
|
+
redis.hgetall(key_for(id))
|
56
|
+
end
|
57
|
+
|
58
|
+
def model_class(record)
|
59
|
+
return self if record["type"].blank?
|
60
|
+
record["type"].constantize
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/modis/index.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
module Modis
|
2
|
+
module Index
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
base.instance_eval do
|
6
|
+
bootstrap_indexes
|
7
|
+
after__internal_create :add_to_index
|
8
|
+
after__internal_update :update_index
|
9
|
+
before__internal_destroy :remove_from_index
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def bootstrap_indexes
|
15
|
+
class << self
|
16
|
+
attr_accessor :indexed_attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
self.indexed_attributes = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def index(attribute)
|
23
|
+
attribute = attribute.to_s
|
24
|
+
raise IndexError, "No such attribute '#{attribute}'" unless attributes.key?(attribute)
|
25
|
+
indexed_attributes << attribute
|
26
|
+
end
|
27
|
+
|
28
|
+
def where(query)
|
29
|
+
raise IndexError, 'Queries using multiple indexes is not currently supported.' if query.keys.size > 1
|
30
|
+
attribute, value = query.first
|
31
|
+
index = index_for(attribute, value)
|
32
|
+
index.map { |id| find(id) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def index_for(attribute, value)
|
36
|
+
Modis.with_connection do |redis|
|
37
|
+
key = index_key(attribute, value)
|
38
|
+
redis.smembers(key).map(&:to_i)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def index_key(attribute, value)
|
43
|
+
"#{absolute_namespace}:index:#{attribute}:#{value.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def indexed_attributes
|
50
|
+
self.class.indexed_attributes
|
51
|
+
end
|
52
|
+
|
53
|
+
def index_key(attribute, value)
|
54
|
+
self.class.index_key(attribute, value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_to_index
|
58
|
+
return if indexed_attributes.empty?
|
59
|
+
|
60
|
+
Modis.with_connection do |redis|
|
61
|
+
indexed_attributes.each do |attribute|
|
62
|
+
key = index_key(attribute, read_attribute(attribute))
|
63
|
+
redis.sadd(key, id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove_from_index
|
69
|
+
return if indexed_attributes.empty?
|
70
|
+
|
71
|
+
Modis.with_connection do |redis|
|
72
|
+
indexed_attributes.each do |attribute|
|
73
|
+
key = index_key(attribute, read_attribute(attribute))
|
74
|
+
redis.srem(key, id)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_index
|
80
|
+
return if indexed_attributes.empty?
|
81
|
+
|
82
|
+
Modis.with_connection do |redis|
|
83
|
+
(changes.keys & indexed_attributes).each do |attribute|
|
84
|
+
old_value, new_value = changes[attribute]
|
85
|
+
old_key = index_key(attribute, old_value)
|
86
|
+
new_key = index_key(attribute, new_value)
|
87
|
+
redis.smove(old_key, new_key, id)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/modis/model.rb
CHANGED
@@ -9,16 +9,15 @@ module Modis
|
|
9
9
|
extend ActiveModel::Naming
|
10
10
|
extend ActiveModel::Callbacks
|
11
11
|
|
12
|
-
define_model_callbacks :save
|
13
|
-
define_model_callbacks :
|
14
|
-
define_model_callbacks :update
|
15
|
-
define_model_callbacks :destroy
|
12
|
+
define_model_callbacks :save, :create, :update, :destroy
|
13
|
+
define_model_callbacks :_internal_create, :_internal_update, :_internal_destroy
|
16
14
|
|
17
15
|
include Modis::Errors
|
18
16
|
include Modis::Transaction
|
19
17
|
include Modis::Persistence
|
20
|
-
include Modis::
|
21
|
-
include Modis::
|
18
|
+
include Modis::Finder
|
19
|
+
include Modis::Attribute
|
20
|
+
include Modis::Index
|
22
21
|
|
23
22
|
base.extend(ClassMethods)
|
24
23
|
end
|
@@ -31,20 +30,19 @@ module Modis
|
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
|
-
def initialize(record=nil, options={})
|
33
|
+
def initialize(record = nil, options = {})
|
35
34
|
set_sti_type
|
36
35
|
apply_defaults
|
37
36
|
assign_attributes(record.symbolize_keys) if record
|
38
37
|
reset_changes
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
end
|
39
|
+
return unless options.key?(:new_record)
|
40
|
+
instance_variable_set('@new_record', options[:new_record])
|
43
41
|
end
|
44
42
|
|
45
43
|
def ==(other)
|
46
44
|
super || other.instance_of?(self.class) && id.present? && other.id == id
|
47
45
|
end
|
48
|
-
|
46
|
+
alias_method :eql?, :==
|
49
47
|
end
|
50
48
|
end
|
data/lib/modis/persistence.rb
CHANGED
@@ -2,21 +2,25 @@ module Modis
|
|
2
2
|
module Persistence
|
3
3
|
def self.included(base)
|
4
4
|
base.extend ClassMethods
|
5
|
+
base.instance_eval do
|
6
|
+
after__internal_create :track
|
7
|
+
before__internal_destroy :untrack
|
8
|
+
end
|
5
9
|
end
|
6
10
|
|
7
11
|
module ClassMethods
|
8
12
|
# :nodoc:
|
9
13
|
def bootstrap_sti(parent, child)
|
10
14
|
child.instance_eval do
|
11
|
-
|
12
|
-
|
15
|
+
parent.instance_eval do
|
16
|
+
class << self
|
13
17
|
attr_accessor :sti_parent
|
14
18
|
end
|
15
|
-
attribute :type, :string
|
19
|
+
attribute :type, :string unless attributes.key?('type')
|
16
20
|
end
|
17
21
|
|
18
22
|
class << self
|
19
|
-
delegate :attributes, to: :sti_parent
|
23
|
+
delegate :attributes, :indexed_attributes, to: :sti_parent
|
20
24
|
end
|
21
25
|
|
22
26
|
@sti_child = true
|
@@ -35,9 +39,7 @@ module Modis
|
|
35
39
|
@namespace = name.split('::').map(&:underscore).join(':')
|
36
40
|
end
|
37
41
|
|
38
|
-
|
39
|
-
@namespace = value
|
40
|
-
end
|
42
|
+
attr_writer :namespace
|
41
43
|
|
42
44
|
def absolute_namespace
|
43
45
|
parts = [Modis.config.namespace, namespace]
|
@@ -49,9 +51,9 @@ module Modis
|
|
49
51
|
end
|
50
52
|
|
51
53
|
def create(attrs)
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
model = new(attrs)
|
55
|
+
model.save
|
56
|
+
model
|
55
57
|
end
|
56
58
|
|
57
59
|
def create!(attrs)
|
@@ -59,6 +61,10 @@ module Modis
|
|
59
61
|
model.save!
|
60
62
|
model
|
61
63
|
end
|
64
|
+
|
65
|
+
def coerce_from_persistence(value)
|
66
|
+
YAML.load(value)
|
67
|
+
end
|
62
68
|
end
|
63
69
|
|
64
70
|
def persisted?
|
@@ -73,29 +79,28 @@ module Modis
|
|
73
79
|
defined?(@new_record) ? @new_record : true
|
74
80
|
end
|
75
81
|
|
76
|
-
def save(args={})
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
false
|
81
|
-
end
|
82
|
+
def save(args = {})
|
83
|
+
create_or_update(args)
|
84
|
+
rescue Modis::RecordInvalid
|
85
|
+
false
|
82
86
|
end
|
83
87
|
|
84
|
-
def save!(args={})
|
88
|
+
def save!(args = {})
|
85
89
|
create_or_update(args) || (raise RecordNotSaved)
|
86
90
|
end
|
87
91
|
|
88
92
|
def destroy
|
89
93
|
self.class.transaction do |redis|
|
90
94
|
run_callbacks :destroy do
|
91
|
-
|
92
|
-
|
95
|
+
run_callbacks :_internal_destroy do
|
96
|
+
redis.del(key)
|
97
|
+
end
|
93
98
|
end
|
94
99
|
end
|
95
100
|
end
|
96
101
|
|
97
102
|
def reload
|
98
|
-
new_attributes = self.class.attributes_for(id)
|
103
|
+
new_attributes = Modis.with_connection { |redis| self.class.attributes_for(redis, id) }
|
99
104
|
initialize(new_attributes)
|
100
105
|
self
|
101
106
|
end
|
@@ -115,36 +120,69 @@ module Modis
|
|
115
120
|
save!
|
116
121
|
end
|
117
122
|
|
118
|
-
|
123
|
+
private
|
119
124
|
|
120
|
-
def
|
121
|
-
|
122
|
-
|
123
|
-
|
125
|
+
def coerce_for_persistence(attribute, value)
|
126
|
+
ensure_type(attribute, value)
|
127
|
+
YAML.dump(value)
|
128
|
+
end
|
129
|
+
|
130
|
+
def ensure_type(attribute, value)
|
131
|
+
type = self.class.attributes[attribute.to_s][:type]
|
132
|
+
type_classes = classes_for_type(type)
|
133
|
+
|
134
|
+
return unless value && !type_classes.include?(value.class)
|
135
|
+
raise Modis::AttributeCoercionError, "Received value of type '#{value.class}', expected '#{type}' for attribute '#{name}'."
|
136
|
+
end
|
137
|
+
|
138
|
+
def classes_for_type(type)
|
139
|
+
{ string: [String],
|
140
|
+
integer: [Fixnum],
|
141
|
+
float: [Float],
|
142
|
+
timestamp: [Time],
|
143
|
+
hash: [Hash],
|
144
|
+
array: [Array],
|
145
|
+
boolean: [TrueClass, FalseClass] }[type]
|
146
|
+
end
|
147
|
+
|
148
|
+
def create_or_update(args = {})
|
149
|
+
validate(args)
|
150
|
+
future = persist
|
151
|
+
|
152
|
+
if future && future.value == 'OK'
|
153
|
+
reset_changes
|
154
|
+
@new_record = false
|
155
|
+
new_record? ? add_to_index : update_index
|
156
|
+
true
|
157
|
+
else
|
158
|
+
false
|
124
159
|
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def validate(args)
|
163
|
+
skip_validate = args.key?(:validate) && args[:validate] == false
|
164
|
+
return if skip_validate || valid?
|
165
|
+
raise Modis::RecordInvalid, errors.full_messages.join(', ')
|
166
|
+
end
|
125
167
|
|
168
|
+
def persist
|
126
169
|
future = nil
|
127
170
|
set_id if new_record?
|
171
|
+
callback = new_record? ? :create : :update
|
128
172
|
|
129
173
|
self.class.transaction do |redis|
|
130
174
|
run_callbacks :save do
|
131
|
-
callback = new_record? ? :create : :update
|
132
175
|
run_callbacks callback do
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
176
|
+
run_callbacks "_internal_#{callback}" do
|
177
|
+
attrs = []
|
178
|
+
attributes.each { |k, v| attrs << k << coerce_for_persistence(k, v) }
|
179
|
+
future = redis.hmset(self.class.key_for(id), attrs)
|
180
|
+
end
|
137
181
|
end
|
138
182
|
end
|
139
183
|
end
|
140
184
|
|
141
|
-
|
142
|
-
reset_changes
|
143
|
-
@new_record = false
|
144
|
-
true
|
145
|
-
else
|
146
|
-
false
|
147
|
-
end
|
185
|
+
future
|
148
186
|
end
|
149
187
|
|
150
188
|
def set_id
|
@@ -153,12 +191,12 @@ module Modis
|
|
153
191
|
end
|
154
192
|
end
|
155
193
|
|
156
|
-
def track
|
157
|
-
redis.sadd(self.class.key_for(:all), id)
|
194
|
+
def track
|
195
|
+
Modis.with_connection { |redis| redis.sadd(self.class.key_for(:all), id) }
|
158
196
|
end
|
159
197
|
|
160
|
-
def untrack
|
161
|
-
redis.srem(self.class.key_for(:all), id)
|
198
|
+
def untrack
|
199
|
+
Modis.with_connection { |redis| redis.srem(self.class.key_for(:all), id) }
|
162
200
|
end
|
163
201
|
end
|
164
202
|
end
|
data/lib/modis/version.rb
CHANGED