HornsAndHooves-moribus 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/.rspec +4 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.simplecov +42 -0
- data/.travis.yml +8 -0
- data/Gemfile +17 -0
- data/HornsAndHooves-moribus.gemspec +31 -0
- data/LICENSE +21 -0
- data/README.md +110 -0
- data/Rakefile +15 -0
- data/lib/colorized_text.rb +33 -0
- data/lib/moribus.rb +138 -0
- data/lib/moribus/aggregated_behavior.rb +80 -0
- data/lib/moribus/aggregated_cache_behavior.rb +76 -0
- data/lib/moribus/alias_association.rb +111 -0
- data/lib/moribus/extensions.rb +37 -0
- data/lib/moribus/extensions/delegate_associated.rb +48 -0
- data/lib/moribus/extensions/has_aggregated_extension.rb +94 -0
- data/lib/moribus/extensions/has_current_extension.rb +17 -0
- data/lib/moribus/macros.rb +135 -0
- data/lib/moribus/tracked_behavior.rb +91 -0
- data/lib/moribus/version.rb +3 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +53 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +31 -0
- data/spec/dummy/config/environments/production.rb +70 -0
- data/spec/dummy/config/environments/test.rb +34 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/moribus/alias_association_spec.rb +88 -0
- data/spec/moribus/macros_spec.rb +7 -0
- data/spec/moribus_spec.rb +332 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/moribus_spec_model.rb +57 -0
- metadata +247 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: de348b300137b8ce46c31b042de38e77a0880a5b
|
4
|
+
data.tar.gz: 675265b328868a116411c59fe89f7f89e369fcdd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0cfa65026b301739a2cb71b0027160f26978b1212f66df68d8dd681815a5e472a0f3a59d737a9e59a8c74cf9df77892093f4e16c32b0b58678880acc04bf3149
|
7
|
+
data.tar.gz: 862712050f65298176c83c3191582b38574d10cdc12025574c8309c3056b9ca472bd1ed93892563037b51ca61ad8a0169cb7be876d9c96a8e6a0ebefb73028c1
|
data/.gitignore
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# simplecov generated
|
2
|
+
coverage
|
3
|
+
coverage.data
|
4
|
+
!coverage/.resultset.json
|
5
|
+
|
6
|
+
# rdoc generated
|
7
|
+
rdoc
|
8
|
+
|
9
|
+
# yard generated
|
10
|
+
doc
|
11
|
+
.yardoc
|
12
|
+
|
13
|
+
# bundler
|
14
|
+
.bundle
|
15
|
+
|
16
|
+
# jeweler generated
|
17
|
+
pkg
|
18
|
+
|
19
|
+
*.gem
|
20
|
+
Gemfile.lock
|
21
|
+
|
22
|
+
# IDE generated
|
23
|
+
.idea
|
24
|
+
|
25
|
+
log/*.log
|
26
|
+
spec/dummy/log/*.log
|
27
|
+
|
28
|
+
# exclude everything in tmp
|
29
|
+
tmp/*
|
30
|
+
# except the metric_fu directory
|
31
|
+
!tmp/metric_fu/
|
32
|
+
# but exclude everything *in* the metric_fu directory
|
33
|
+
tmp/metric_fu/*
|
34
|
+
# except for the _data directory to track metrical outputs
|
35
|
+
!tmp/metric_fu/_data/
|
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
moribus
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.2
|
data/.simplecov
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "simplecov-rcov-text"
|
2
|
+
require "colorized_text"
|
3
|
+
include ColorizedText
|
4
|
+
|
5
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
6
|
+
SimpleCov::Formatter::RcovTextFormatter,
|
7
|
+
SimpleCov::Formatter::HTMLFormatter
|
8
|
+
]
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter "/spec/"
|
11
|
+
|
12
|
+
# Fail the build when coverage is weak:
|
13
|
+
at_exit do
|
14
|
+
SimpleCov.result.format!
|
15
|
+
threshold, actual = 98.475, SimpleCov.result.covered_percent
|
16
|
+
if actual < threshold
|
17
|
+
msg = "\nLow coverage: "
|
18
|
+
msg << red("#{actual}%")
|
19
|
+
msg << " is #{red 'under'} the threshold: "
|
20
|
+
msg << green("#{threshold}%.")
|
21
|
+
msg << "\n"
|
22
|
+
$stderr.puts msg
|
23
|
+
exit 1
|
24
|
+
else
|
25
|
+
# Precision: three decimal places:
|
26
|
+
actual_trunc = (actual * 1000).floor / 1000.0
|
27
|
+
msg = "\nCoverage: "
|
28
|
+
msg << green("#{actual}%")
|
29
|
+
msg << " is #{green 'over'} the threshold: "
|
30
|
+
if actual_trunc > threshold
|
31
|
+
msg << bold(yellow("#{threshold}%. "))
|
32
|
+
msg << "Please update the threshold to: "
|
33
|
+
msg << bold(green("#{actual_trunc}% "))
|
34
|
+
msg << "in ./.simplecov."
|
35
|
+
else
|
36
|
+
msg << green("#{threshold}%.")
|
37
|
+
end
|
38
|
+
msg << "\n"
|
39
|
+
$stdout.puts msg
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in moribus.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "redcarpet"
|
8
|
+
gem "yard"
|
9
|
+
gem "pry"
|
10
|
+
end
|
11
|
+
|
12
|
+
group :test do
|
13
|
+
gem "simplecov", require: false
|
14
|
+
gem "simplecov-rcov-text", require: false
|
15
|
+
|
16
|
+
gem "timecop"
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
$:.push File.expand_path("../lib", __FILE__)
|
4
|
+
require "moribus/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "HornsAndHooves-moribus"
|
8
|
+
s.version = Moribus::VERSION
|
9
|
+
s.authors = ["HornsAndHooves", "Artem Kuzko", "Sergey Potapov"]
|
10
|
+
s.email = ["a.kuzko@gmail.com", "blake131313@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/HornsAndHooves/moribus"
|
12
|
+
s.licenses = ["MIT"]
|
13
|
+
s.summary = %q{Introduces Aggregated and Tracked behavior to ActiveRecord::Base models}
|
14
|
+
s.description = %q{Introduces Aggregated and Tracked behavior to ActiveRecord::Base models, as well
|
15
|
+
as Macros and Extensions modules for more efficient usage.}
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# specify any dependencies here; for example:
|
23
|
+
s.add_dependency "rails", "~> 4.0.5"
|
24
|
+
s.add_dependency "power_enum", ">= 2.7.0"
|
25
|
+
s.add_dependency "yard", ">= 0"
|
26
|
+
|
27
|
+
s.add_development_dependency "rake"
|
28
|
+
s.add_development_dependency "rspec"
|
29
|
+
s.add_development_dependency "rspec-rails"
|
30
|
+
s.add_development_dependency "sqlite3"
|
31
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 HornsAndHooves
|
4
|
+
Copyright (c) 2013 TMXCredit, authors Artem Kuzko, Sergey Potapov
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
7
|
+
this software and associated documentation files (the "Software"), to deal in
|
8
|
+
the Software without restriction, including without limitation the rights to
|
9
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
10
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
11
|
+
subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
18
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
19
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
20
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Moribus
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/HornsAndHooves/moribus.png)](http://travis-ci.org/HornsAndHooves/moribus)
|
4
|
+
|
5
|
+
Moribus is a set of tools for managing complex graphs of ActiveRecord objects
|
6
|
+
for which there are many inbound foreign keys, attributes and associations with
|
7
|
+
high rates of change, and business demands for well-tracked change history.
|
8
|
+
|
9
|
+
##AggregatedBehavior
|
10
|
+
|
11
|
+
AggregatedBehavior implements a pattern in which an object's identity
|
12
|
+
is modeled apart from its attributes and outbound associations. This enables
|
13
|
+
a higher level of normalization of your data, since one set of properties
|
14
|
+
may be shared among multiple objects. This set of properties - attributes
|
15
|
+
and outbound associations - are modeled on an object called an "info object".
|
16
|
+
And we say that this info object is aggregated by host object(s) and it acts
|
17
|
+
(behaves) as aggregated. When an aggregated object is about to be saved, it
|
18
|
+
looks up for an existing record with the same attributes in the database under
|
19
|
+
the hood, and if it is found, it 'replaces' itself with that record. This allows
|
20
|
+
you to work with attributes of your entity as if they are properties of an
|
21
|
+
actual model and normalize your data at the same time.
|
22
|
+
|
23
|
+
Inbound foreign keys will always point at the same object in memory, and the
|
24
|
+
object will never be stale, as it has no attributes of its own that are subject
|
25
|
+
to change. This is useful for objects with many inbound foreign keys and
|
26
|
+
high-traffic attributes/associations, such as statuses. Without this pattern
|
27
|
+
it would be difficult to avoid many StaleObjectErrors.
|
28
|
+
|
29
|
+
##TrackedBehavior
|
30
|
+
|
31
|
+
TrackedBehavior implements history tracking on the stack of objects
|
32
|
+
representing the identity object's attributes and outbound associations.
|
33
|
+
When a model behaves as a tracked behavior, it will never actually get
|
34
|
+
updated. Instead, it will update it's own 'is_current' column to false
|
35
|
+
and will be saved as a new record with new attribute values and the
|
36
|
+
'is_current' column as 'true'. Thus, under the hood, new attributes
|
37
|
+
will supersede old attributes, leaving the old record as historical.
|
38
|
+
|
39
|
+
##Macros, Associations and Combination
|
40
|
+
|
41
|
+
Despite the fact that Moribus may be used by models on its own,
|
42
|
+
its main purpose is to be used within associations, and in conjunction
|
43
|
+
with associations. The best way to demonstrate this is by example.
|
44
|
+
|
45
|
+
Let's assume we have a User entity with attributes that should be tracked
|
46
|
+
and normalized. Those attributes may be, for example, `:first_name`,
|
47
|
+
`:last_name` and `:status` as enumerated integer value. This entity
|
48
|
+
may be represented with three models: `User` - with main model for interactions,
|
49
|
+
tracked `UserInfo` (`user_id`, `person_name_id`, `status`) for tracking, and
|
50
|
+
aggregated `UserName` (`first_name`, `last_name`) for name normalization.
|
51
|
+
Class definitions for these models will appear as follows:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class User < ActiveRecord::Base
|
55
|
+
has_one_current :user_info
|
56
|
+
delegate_associated :user_name, :to => :user_info
|
57
|
+
end
|
58
|
+
|
59
|
+
class UserInfo < ActiveRecord::Base
|
60
|
+
has_aggregated :person_name
|
61
|
+
acts_as_tracked
|
62
|
+
end
|
63
|
+
|
64
|
+
class UserName < ActiveRecord::Base
|
65
|
+
acts_as_aggregated
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
Despite the fact that internal representation is more complicated now,
|
70
|
+
top-level operations will look exactly the same:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
user = User.create(:first_name => 'John', :last_name => 'Smith', :status => 0)
|
74
|
+
# This creates User(id: 1) record, PersonName(id: 1, first_name: 'John', last_name: 'Smith')
|
75
|
+
# record and UserInfo(id: 1, user_id: 1, person_name_id: 1, status: 0, is_current: true)
|
76
|
+
|
77
|
+
user.update_attributes(:status => 1)
|
78
|
+
# This creates new UserInfo(id: 2, user_id: 1, person_name_id: 1, status: 1, is_current: true)
|
79
|
+
# record, and changes UserInfo(id: 1) record's 'is_current' attribute to false.
|
80
|
+
|
81
|
+
user.update_attributes(:first_name => 'Mike')
|
82
|
+
# This creates new PersonName(id: 2, first_name: 'Mike', last_name: 'Smith') record and new
|
83
|
+
# current UserInfo(id: 3, user_id: 1, person_name_id: 2, :status: 1, is_current: true)
|
84
|
+
|
85
|
+
# Now, if we want to create another 'John Smith' user:
|
86
|
+
user2 = User.create(:first_name => 'John', :last_name => 'Smith', :status => 5)
|
87
|
+
# This creates User(id: 2) record and UserInfo(id: 4, user_id: 2, person_name_id: 1, status: 5, is_current: true)
|
88
|
+
# record that reuses existing UserName information.
|
89
|
+
```
|
90
|
+
|
91
|
+
## Use it
|
92
|
+
|
93
|
+
gem 'HornsAndHooves-moribus', require: 'moribus'
|
94
|
+
|
95
|
+
## Run tests
|
96
|
+
|
97
|
+
```sh
|
98
|
+
rake spec
|
99
|
+
```
|
100
|
+
|
101
|
+
## Credits
|
102
|
+
|
103
|
+
* [Artem Kuzko](https://github.com/akuzko)
|
104
|
+
* [Potapov Sergey](https://github.com/greyblake)
|
105
|
+
|
106
|
+
## Copyright
|
107
|
+
|
108
|
+
Copyright (c) 2014 HornsAndHooves.
|
109
|
+
|
110
|
+
Copyright (c) 2013 TMX Credit.
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
task :default => :spec
|
5
|
+
RSpec::Core::RakeTask.new
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
Rake::RDocTask.new do |rdoc|
|
9
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
10
|
+
|
11
|
+
rdoc.rdoc_dir = 'rdoc'
|
12
|
+
rdoc.title = "moribus #{version}"
|
13
|
+
rdoc.rdoc_files.include('README*')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Colorizes text with ASCII colors.
|
2
|
+
# == Usage:
|
3
|
+
# include ColorizedText
|
4
|
+
#
|
5
|
+
# puts green "OK" # => green output
|
6
|
+
# puts bold "Running... # => bold output
|
7
|
+
# puts bold green "OK!!!" # => bold green output
|
8
|
+
module ColorizedText
|
9
|
+
# Colorize text using ASCII color code
|
10
|
+
def colorize(text, code)
|
11
|
+
"\033[#{code}m#{text}\033[0m"
|
12
|
+
end
|
13
|
+
|
14
|
+
# :nodoc:
|
15
|
+
def yellow(text)
|
16
|
+
colorize(text, 33)
|
17
|
+
end
|
18
|
+
|
19
|
+
# :nodoc:
|
20
|
+
def green(text)
|
21
|
+
colorize(text, 32)
|
22
|
+
end
|
23
|
+
|
24
|
+
# :nodoc:
|
25
|
+
def red(text)
|
26
|
+
colorize(text, 31)
|
27
|
+
end
|
28
|
+
|
29
|
+
# :nodoc:
|
30
|
+
def bold(text)
|
31
|
+
colorize(text, 1)
|
32
|
+
end
|
33
|
+
end
|
data/lib/moribus.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'power_enum'
|
2
|
+
|
3
|
+
# Introduces Aggregated and Tracked behavior to ActiveRecord::Base models, as well
|
4
|
+
# as Macros and Extensions modules for more efficient usage. Effectively replaces
|
5
|
+
# both Aggregatable and Trackable modules.
|
6
|
+
module Moribus
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
|
10
|
+
autoload :AliasAssociation
|
11
|
+
autoload :AggregatedBehavior
|
12
|
+
autoload :AggregatedCacheBehavior
|
13
|
+
autoload :TrackedBehavior
|
14
|
+
autoload :Macros
|
15
|
+
autoload :Extensions
|
16
|
+
|
17
|
+
included do
|
18
|
+
include AliasAssociation
|
19
|
+
include Extensions
|
20
|
+
extend Macros
|
21
|
+
end
|
22
|
+
|
23
|
+
# :nodoc:
|
24
|
+
module ClassMethods
|
25
|
+
# Adds aggregated behavior to a model.
|
26
|
+
def acts_as_aggregated(options = {})
|
27
|
+
options.symbolize_keys!
|
28
|
+
|
29
|
+
options.assert_valid_keys(:cache_by, :non_content_columns)
|
30
|
+
include AggregatedBehavior
|
31
|
+
|
32
|
+
if options[:cache_by].present?
|
33
|
+
@aggregated_caching_column = options[:cache_by]
|
34
|
+
include AggregatedCacheBehavior
|
35
|
+
end
|
36
|
+
|
37
|
+
if options[:non_content_columns]
|
38
|
+
self.aggregated_behaviour_non_content_columns += Array.wrap(options[:non_content_columns]).map(&:to_s)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
private :acts_as_aggregated
|
42
|
+
|
43
|
+
# Adds tracked behavior to a model
|
44
|
+
def acts_as_tracked(options = {})
|
45
|
+
options.symbolize_keys!
|
46
|
+
|
47
|
+
options.assert_valid_keys(:preceding_key)
|
48
|
+
include TrackedBehavior
|
49
|
+
|
50
|
+
@preceding_key_column = options[:preceding_key]
|
51
|
+
end
|
52
|
+
private :acts_as_tracked
|
53
|
+
|
54
|
+
# Return +true+ if self was declared as +acts_as_aggregated+.
|
55
|
+
def acts_as_aggregated?
|
56
|
+
self < AggregatedBehavior
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return +true+ if self was declared as +acts_as_tracked+.
|
60
|
+
def acts_as_tracked?
|
61
|
+
self < TrackedBehavior
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Marks +self+ as a new record. Sets +id+ attribute to nil, but memorizes
|
66
|
+
# the old value in case of exception.
|
67
|
+
def to_new_record!
|
68
|
+
store_before_to_new_record_values
|
69
|
+
reset_persistence_values
|
70
|
+
@new_record = true
|
71
|
+
end
|
72
|
+
|
73
|
+
# Marks +self+ as persistent record. If another record is passed, uses its
|
74
|
+
# persistence attributes (id, timestamps). If nil is passed as an argument,
|
75
|
+
# marks +self+ as persisted record and sets +id+ to memorized value.
|
76
|
+
def to_persistent!(existing = nil)
|
77
|
+
if existing
|
78
|
+
self.id = existing.id
|
79
|
+
self.created_at = existing.created_at if respond_to?(:created_at)
|
80
|
+
self.updated_at = existing.updated_at if respond_to?(:updated_at)
|
81
|
+
@changed_attributes = {}
|
82
|
+
else
|
83
|
+
restore_before_to_new_record_values
|
84
|
+
end
|
85
|
+
@new_record = false
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
# Stores original record id for tracking purposes.
|
90
|
+
def set_parent
|
91
|
+
tbc = self.class.preceding_key_column
|
92
|
+
if tbc && self.respond_to?(tbc)
|
93
|
+
write_attribute(tbc, @_before_to_new_record_values[:id])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Helper method used by has_aggregated (in fact, belongs_to)
|
98
|
+
# association during autosave.
|
99
|
+
def updated_as_aggregated?
|
100
|
+
!!@updated_as_aggregated
|
101
|
+
end
|
102
|
+
|
103
|
+
# Save persistence values of id, updated_at and created_at to instance
|
104
|
+
# variable to have an ability to set them back if object fails to
|
105
|
+
# be saved.
|
106
|
+
def store_before_to_new_record_values
|
107
|
+
values = {:id => id}
|
108
|
+
values[:updated_at] = updated_at if respond_to?(:updated_at)
|
109
|
+
values[:created_at] = created_at if respond_to?(:created_at)
|
110
|
+
@_before_to_new_record_values = values
|
111
|
+
end
|
112
|
+
private :store_before_to_new_record_values
|
113
|
+
|
114
|
+
# Set persistence values of id, updated_at and created_at back.
|
115
|
+
def restore_before_to_new_record_values
|
116
|
+
values = @_before_to_new_record_values
|
117
|
+
self.id = values[:id]
|
118
|
+
self.created_at = values[:created_at] if respond_to?(:updated_at=)
|
119
|
+
self.updated_at = values[:updated_at] if respond_to?(:updated_at=)
|
120
|
+
end
|
121
|
+
private :restore_before_to_new_record_values
|
122
|
+
|
123
|
+
# Set id, updated_at and created_at to nil in order to
|
124
|
+
# update them when new record is created.
|
125
|
+
def reset_persistence_values
|
126
|
+
self.id = nil
|
127
|
+
self.updated_at = nil if respond_to?(:updated_at=)
|
128
|
+
self.created_at = nil if respond_to?(:created_at=)
|
129
|
+
|
130
|
+
# mark all other attributes is changing
|
131
|
+
(attributes.keys - changes.keys).each{ |key| self.send(:"#{key}_will_change!") }
|
132
|
+
end
|
133
|
+
private :reset_persistence_values
|
134
|
+
end
|
135
|
+
|
136
|
+
ActiveRecord::Base.class_eval do
|
137
|
+
include Moribus
|
138
|
+
end
|