bookie_accounting 2.0.0 → 2.0.1
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Rakefile +0 -7
- data/TODO +13 -0
- data/bookie_accounting.gemspec +1 -9
- data/lib/bookie/database.rb +17 -19
- data/lib/bookie/database/group.rb +3 -3
- data/lib/bookie/database/job.rb +21 -18
- data/lib/bookie/database/job_summary.rb +26 -29
- data/lib/bookie/database/lock.rb +4 -4
- data/lib/bookie/database/system_type.rb +6 -6
- data/lib/bookie/database/user.rb +5 -5
- data/lib/bookie/formatters/stdout.rb +9 -7
- data/lib/bookie/version.rb +1 -1
- data/spec/config_spec.rb +21 -21
- data/spec/database/group_spec.rb +13 -14
- data/spec/database/job_spec.rb +62 -63
- data/spec/database/job_summary_spec.rb +60 -62
- data/spec/database/lock_spec.rb +11 -12
- data/spec/database/system_spec.rb +49 -50
- data/spec/database/system_type_spec.rb +14 -15
- data/spec/database/user_spec.rb +25 -26
- data/spec/extensions_spec.rb +11 -11
- data/spec/formatter_spec.rb +15 -15
- data/spec/formatters/comma_dump_spec.rb +8 -8
- data/spec/formatters/spreadsheet_spec.rb +9 -9
- data/spec/formatters/stdout_spec.rb +8 -8
- data/spec/sender_spec.rb +44 -46
- data/spec/senders/standalone_spec.rb +9 -11
- data/spec/senders/torque_cluster_spec.rb +31 -31
- metadata +14 -43
- data/todo.txt +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c891f54836ddb4da00b36831ab8a62dcea1b8cb
|
|
4
|
+
data.tar.gz: 5afc39ea463dcf38dfee8d4526e83185b160b697
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ad6f4d440ef1c1537a584aa99937bcee203227b0d233805405210e92398dd87c00b7f870fedaefb04ed6de3d39961b682b3c32df2453154389ffbfe1a320c99
|
|
7
|
+
data.tar.gz: e28d51f6afa886edc644e8a526473efd2b9dd26db271e82b543a4c289bc251ab4e4721af5eb3fe2682cfd1598a458031b8287825198d2976b987b07ed3e08b87
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
|
@@ -10,13 +10,6 @@ task :default => :spec
|
|
|
10
10
|
desc "Run specs"
|
|
11
11
|
RSpec::Core::RakeTask.new(:spec) do |task|
|
|
12
12
|
task.rspec_opts =%w{--color --order rand --format progress}
|
|
13
|
-
paths = []
|
|
14
|
-
Find.find('spec') do |path|
|
|
15
|
-
if File.directory?(path)
|
|
16
|
-
paths << "#{path}/*_spec.rb"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
task.pattern = paths.join(" ")
|
|
20
13
|
end
|
|
21
14
|
|
|
22
15
|
task :rdoc do
|
data/TODO
CHANGED
|
@@ -1 +1,14 @@
|
|
|
1
|
+
Use bulk inserts?
|
|
2
|
+
Replace by_* methods with a "find_all_by()" method? (or just "where"...)
|
|
3
|
+
Allow configuration of date boundary to midnight in local time zone?
|
|
4
|
+
Make sure all source files have the required "require"s
|
|
5
|
+
Use "let" in RSpect tests?
|
|
6
|
+
Replace 3600 with 2.hours, etc.
|
|
7
|
+
Also clear out other "magic" numbers.
|
|
8
|
+
Split some tests up.
|
|
9
|
+
Validate uniqueness?
|
|
10
|
+
Unit test uniqueness constraints?
|
|
11
|
+
Use optimistic concurrency control?
|
|
12
|
+
Make units more independent.
|
|
13
|
+
Make unit test success more independent.
|
|
1
14
|
|
data/bookie_accounting.gemspec
CHANGED
|
@@ -15,18 +15,10 @@ Gem::Specification.new do |gem|
|
|
|
15
15
|
gem.name = "bookie_accounting"
|
|
16
16
|
gem.require_paths = ["lib"]
|
|
17
17
|
gem.version = Bookie::VERSION
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
gem.add_dependency('activerecord')
|
|
20
20
|
gem.add_dependency('json')
|
|
21
|
-
#We need this because Bundler has no concept of optional dependencies
|
|
22
|
-
#and complains about using non-dependency gems.
|
|
23
|
-
#TODO: figure out how to remove
|
|
24
|
-
gem.add_dependency('mysql2')
|
|
25
21
|
gem.add_dependency('pacct')
|
|
26
|
-
#Introduces the old ActiveRecord mass assignment security methods
|
|
27
|
-
#(until I update the database code for the new methods)
|
|
28
|
-
#TODO: get rid of this.
|
|
29
|
-
gem.add_dependency('protected_attributes')
|
|
30
22
|
gem.add_dependency('spreadsheet')
|
|
31
23
|
end
|
|
32
24
|
|
data/lib/bookie/database.rb
CHANGED
|
@@ -2,8 +2,6 @@ require 'bookie/config'
|
|
|
2
2
|
require 'bookie/extensions'
|
|
3
3
|
|
|
4
4
|
require 'active_record'
|
|
5
|
-
#TODO: remove when code is updated.
|
|
6
|
-
require 'protected_attributes'
|
|
7
5
|
|
|
8
6
|
require 'bookie/database/job'
|
|
9
7
|
require 'bookie/database/job_summary'
|
|
@@ -21,7 +19,7 @@ module Bookie
|
|
|
21
19
|
#- <tt>:avg => 1</tt>
|
|
22
20
|
#- <tt>:max => 2</tt>
|
|
23
21
|
#
|
|
24
|
-
|
|
22
|
+
|
|
25
23
|
##
|
|
26
24
|
#Database migrations
|
|
27
25
|
module Migration
|
|
@@ -35,12 +33,12 @@ module Bookie
|
|
|
35
33
|
t.index [:name, :group_id], :unique => true
|
|
36
34
|
end
|
|
37
35
|
end
|
|
38
|
-
|
|
36
|
+
|
|
39
37
|
def down
|
|
40
38
|
drop_table :users
|
|
41
39
|
end
|
|
42
40
|
end
|
|
43
|
-
|
|
41
|
+
|
|
44
42
|
class CreateGroups < ActiveRecord::Migration
|
|
45
43
|
def up
|
|
46
44
|
create_table :groups do |t|
|
|
@@ -50,12 +48,12 @@ module Bookie
|
|
|
50
48
|
t.index :name, :unique => true
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
|
-
|
|
51
|
+
|
|
54
52
|
def down
|
|
55
53
|
drop_table :groups
|
|
56
54
|
end
|
|
57
55
|
end
|
|
58
|
-
|
|
56
|
+
|
|
59
57
|
class CreateSystems < ActiveRecord::Migration
|
|
60
58
|
def up
|
|
61
59
|
create_table :systems do |t|
|
|
@@ -73,12 +71,12 @@ module Bookie
|
|
|
73
71
|
t.index :system_type_id
|
|
74
72
|
end
|
|
75
73
|
end
|
|
76
|
-
|
|
74
|
+
|
|
77
75
|
def down
|
|
78
76
|
drop_table :systems
|
|
79
77
|
end
|
|
80
78
|
end
|
|
81
|
-
|
|
79
|
+
|
|
82
80
|
class CreateSystemTypes < ActiveRecord::Migration
|
|
83
81
|
def up
|
|
84
82
|
create_table :system_types do |t|
|
|
@@ -89,12 +87,12 @@ module Bookie
|
|
|
89
87
|
t.index :name, :unique => true
|
|
90
88
|
end
|
|
91
89
|
end
|
|
92
|
-
|
|
90
|
+
|
|
93
91
|
def down
|
|
94
92
|
drop_table :system_types
|
|
95
93
|
end
|
|
96
94
|
end
|
|
97
|
-
|
|
95
|
+
|
|
98
96
|
class CreateJobs < ActiveRecord::Migration
|
|
99
97
|
def up
|
|
100
98
|
create_table :jobs do |t|
|
|
@@ -118,12 +116,12 @@ module Bookie
|
|
|
118
116
|
t.index :exit_code
|
|
119
117
|
end
|
|
120
118
|
end
|
|
121
|
-
|
|
119
|
+
|
|
122
120
|
def down
|
|
123
121
|
drop_table :jobs
|
|
124
122
|
end
|
|
125
123
|
end
|
|
126
|
-
|
|
124
|
+
|
|
127
125
|
class CreateJobSummaries < ActiveRecord::Migration
|
|
128
126
|
def up
|
|
129
127
|
create_table :job_summaries do |t|
|
|
@@ -140,12 +138,12 @@ module Bookie
|
|
|
140
138
|
t.index :date
|
|
141
139
|
end
|
|
142
140
|
end
|
|
143
|
-
|
|
141
|
+
|
|
144
142
|
def down
|
|
145
143
|
drop_table :job_summaries
|
|
146
144
|
end
|
|
147
145
|
end
|
|
148
|
-
|
|
146
|
+
|
|
149
147
|
class CreateLocks < ActiveRecord::Migration
|
|
150
148
|
def up
|
|
151
149
|
create_table :locks do |t|
|
|
@@ -154,17 +152,17 @@ module Bookie
|
|
|
154
152
|
change_table :locks do |t|
|
|
155
153
|
t.index :name, :unique => true
|
|
156
154
|
end
|
|
157
|
-
|
|
155
|
+
|
|
158
156
|
['users', 'groups', 'systems', 'system_types', 'job_summaries'].each do |name|
|
|
159
157
|
Lock.create!(:name => name)
|
|
160
158
|
end
|
|
161
159
|
end
|
|
162
|
-
|
|
160
|
+
|
|
163
161
|
def down
|
|
164
162
|
drop_table :locks
|
|
165
163
|
end
|
|
166
164
|
end
|
|
167
|
-
|
|
165
|
+
|
|
168
166
|
class << self;
|
|
169
167
|
##
|
|
170
168
|
#Brings up all migrations
|
|
@@ -178,7 +176,7 @@ module Bookie
|
|
|
178
176
|
CreateJobSummaries.new.up
|
|
179
177
|
CreateLocks.new.up
|
|
180
178
|
end
|
|
181
|
-
|
|
179
|
+
|
|
182
180
|
##
|
|
183
181
|
#Brings down all migrations
|
|
184
182
|
#
|
|
@@ -19,14 +19,14 @@ module Bookie
|
|
|
19
19
|
group = known_groups[name] if known_groups
|
|
20
20
|
unless group
|
|
21
21
|
Lock[:groups].synchronize do
|
|
22
|
-
group =
|
|
23
|
-
group ||= create!(:
|
|
22
|
+
group = find_by(name: name)
|
|
23
|
+
group ||= create!(name: name)
|
|
24
24
|
end
|
|
25
25
|
known_groups[name] = group if known_groups
|
|
26
26
|
end
|
|
27
27
|
group
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
validates_presence_of :name
|
|
31
31
|
end
|
|
32
32
|
end
|
data/lib/bookie/database/job.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Bookie
|
|
|
21
21
|
belongs_to :system
|
|
22
22
|
has_one :group, :through => :user
|
|
23
23
|
has_one :system_type, :through => :system
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
##
|
|
26
26
|
#The time at which the job ended
|
|
27
27
|
def end_time
|
|
@@ -33,17 +33,17 @@ module Bookie
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
#To consider: disable #end_time= ?
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
def self.by_user(user)
|
|
38
38
|
where('jobs.user_id = ?', user.id)
|
|
39
39
|
end
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
##
|
|
42
42
|
#Filters by user name
|
|
43
43
|
def self.by_user_name(user_name)
|
|
44
44
|
joins(:user).where('users.name = ?', user_name)
|
|
45
45
|
end
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
def self.by_system(system)
|
|
48
48
|
where('jobs.system_id = ?', system.id)
|
|
49
49
|
end
|
|
@@ -53,27 +53,30 @@ module Bookie
|
|
|
53
53
|
def self.by_system_name(system_name)
|
|
54
54
|
joins(:system).where('systems.name = ?', system_name)
|
|
55
55
|
end
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
##
|
|
58
58
|
#Filters by group name
|
|
59
59
|
def self.by_group_name(group_name)
|
|
60
|
-
group = Group.
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
group = Group.find_by(name: group_name)
|
|
61
|
+
if group then
|
|
62
|
+
joins(:user).where('users.group_id = ?', group.id)
|
|
63
|
+
else
|
|
64
|
+
self.none
|
|
65
|
+
end
|
|
63
66
|
end
|
|
64
|
-
|
|
67
|
+
|
|
65
68
|
##
|
|
66
69
|
#Filters by system type
|
|
67
70
|
def self.by_system_type(system_type)
|
|
68
71
|
joins(:system).where('systems.system_type_id = ?', system_type.id)
|
|
69
72
|
end
|
|
70
|
-
|
|
73
|
+
|
|
71
74
|
##
|
|
72
75
|
#Filters by command name
|
|
73
76
|
def self.by_command_name(c_name)
|
|
74
77
|
where('jobs.command_name = ?', c_name)
|
|
75
78
|
end
|
|
76
|
-
|
|
79
|
+
|
|
77
80
|
##
|
|
78
81
|
#Finds all jobs that were running at some point in a given time range
|
|
79
82
|
def self.by_time_range(time_range)
|
|
@@ -100,7 +103,7 @@ module Bookie
|
|
|
100
103
|
where('? <= jobs.start_time AND jobs.end_time <= ?', time_range.begin, time_range.end)
|
|
101
104
|
end
|
|
102
105
|
end
|
|
103
|
-
|
|
106
|
+
|
|
104
107
|
##
|
|
105
108
|
#Finds all jobs that overlap the edges of the given time range
|
|
106
109
|
def self.overlapping_edges(time_range)
|
|
@@ -112,7 +115,7 @@ module Bookie
|
|
|
112
115
|
where(query_str, {:begin => time_range.begin, :end => time_range.end})
|
|
113
116
|
end
|
|
114
117
|
end
|
|
115
|
-
|
|
118
|
+
|
|
116
119
|
##
|
|
117
120
|
#Produces a summary of the jobs in the given time interval
|
|
118
121
|
#
|
|
@@ -177,22 +180,22 @@ module Bookie
|
|
|
177
180
|
:memory_time => memory_time,
|
|
178
181
|
}
|
|
179
182
|
end
|
|
180
|
-
|
|
183
|
+
|
|
181
184
|
before_save do
|
|
182
185
|
write_attribute(:end_time, end_time)
|
|
183
186
|
end
|
|
184
|
-
|
|
187
|
+
|
|
185
188
|
before_update do
|
|
186
189
|
write_attribute(:end_time, end_time)
|
|
187
190
|
end
|
|
188
|
-
|
|
191
|
+
|
|
189
192
|
validates_presence_of :user, :system, :cpu_time,
|
|
190
193
|
:start_time, :wall_time, :memory, :exit_code
|
|
191
|
-
|
|
194
|
+
|
|
192
195
|
validates_each :command_name do |record, attr, value|
|
|
193
196
|
record.errors.add(attr, 'must not be nil') if value == nil
|
|
194
197
|
end
|
|
195
|
-
|
|
198
|
+
|
|
196
199
|
validates_each :cpu_time, :wall_time, :memory do |record, attr, value|
|
|
197
200
|
record.errors.add(attr, 'must be a non-negative integer') unless value && value >= 0
|
|
198
201
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'active_record'
|
|
2
|
-
require 'protected_attributes'
|
|
3
2
|
|
|
4
3
|
require 'bookie/database/job'
|
|
5
4
|
require 'bookie/database/system'
|
|
@@ -13,12 +12,10 @@ module Bookie
|
|
|
13
12
|
#Most summary operations should be performed through this class to improve efficiency.
|
|
14
13
|
class JobSummary < ActiveRecord::Base
|
|
15
14
|
self.table_name = :job_summaries
|
|
16
|
-
|
|
15
|
+
|
|
17
16
|
belongs_to :user
|
|
18
17
|
belongs_to :system
|
|
19
18
|
|
|
20
|
-
attr_accessible :date, :user, :user_id, :system, :system_id, :command_name, :cpu_time, :memory_time
|
|
21
|
-
|
|
22
19
|
##
|
|
23
20
|
#Filters by date
|
|
24
21
|
def self.by_date(date)
|
|
@@ -35,25 +32,25 @@ module Bookie
|
|
|
35
32
|
where('? <= job_summaries.date AND job_summaries.date <= ?', range.begin, range.end)
|
|
36
33
|
end
|
|
37
34
|
end
|
|
38
|
-
|
|
35
|
+
|
|
39
36
|
##
|
|
40
37
|
#Filters by user
|
|
41
38
|
def self.by_user(user)
|
|
42
39
|
where('job_summaries.user_id = ?', user.id)
|
|
43
40
|
end
|
|
44
|
-
|
|
41
|
+
|
|
45
42
|
##
|
|
46
43
|
#Filters by user name
|
|
47
44
|
def self.by_user_name(name)
|
|
48
45
|
joins(:user).where('users.name = ?', name)
|
|
49
46
|
end
|
|
50
|
-
|
|
47
|
+
|
|
51
48
|
##
|
|
52
49
|
#Filters by group
|
|
53
50
|
def self.by_group(group)
|
|
54
51
|
joins(:user).where('users.group_id = ?', group.id)
|
|
55
52
|
end
|
|
56
|
-
|
|
53
|
+
|
|
57
54
|
##
|
|
58
55
|
#Filters by group name
|
|
59
56
|
def self.by_group_name(name)
|
|
@@ -64,31 +61,31 @@ module Bookie
|
|
|
64
61
|
self.none
|
|
65
62
|
end
|
|
66
63
|
end
|
|
67
|
-
|
|
64
|
+
|
|
68
65
|
##
|
|
69
66
|
#Filters by system
|
|
70
67
|
def self.by_system(system)
|
|
71
68
|
where('job_summaries.system_id = ?', system.id)
|
|
72
69
|
end
|
|
73
|
-
|
|
70
|
+
|
|
74
71
|
##
|
|
75
72
|
#Filters by system name
|
|
76
73
|
def self.by_system_name(name)
|
|
77
74
|
joins(:system).where('systems.name = ?', name)
|
|
78
75
|
end
|
|
79
|
-
|
|
76
|
+
|
|
80
77
|
##
|
|
81
78
|
#Filters by system type
|
|
82
79
|
def self.by_system_type(type)
|
|
83
80
|
joins(:system).where('systems.system_type_id = ?', type.id)
|
|
84
81
|
end
|
|
85
|
-
|
|
82
|
+
|
|
86
83
|
##
|
|
87
84
|
#Filters by command name
|
|
88
85
|
def self.by_command_name(cmd)
|
|
89
86
|
where('job_summaries.command_name = ?', cmd)
|
|
90
87
|
end
|
|
91
|
-
|
|
88
|
+
|
|
92
89
|
##
|
|
93
90
|
#Create cached summaries for the given date
|
|
94
91
|
#
|
|
@@ -100,12 +97,12 @@ module Bookie
|
|
|
100
97
|
def self.summarize(date)
|
|
101
98
|
jobs = Job
|
|
102
99
|
unscoped = self.unscoped
|
|
103
|
-
time_min = date.to_utc_time
|
|
100
|
+
time_min = date.to_utc_time
|
|
104
101
|
time_range = time_min ... time_min + 1.days
|
|
105
102
|
day_jobs = jobs.by_time_range(time_range)
|
|
106
103
|
|
|
107
104
|
#Find the unique combinations of values for some of the jobs' attributes.
|
|
108
|
-
value_sets = day_jobs.
|
|
105
|
+
value_sets = day_jobs.uniq.pluck(:user_id, :system_id, :command_name)
|
|
109
106
|
if value_sets.empty?
|
|
110
107
|
#There are no jobs, so create a dummy summary.
|
|
111
108
|
user = User.select(:id).first
|
|
@@ -128,17 +125,17 @@ module Bookie
|
|
|
128
125
|
else
|
|
129
126
|
value_sets.each do |set|
|
|
130
127
|
summary_jobs = jobs.where(
|
|
131
|
-
:user_id => set
|
|
132
|
-
:system_id => set
|
|
133
|
-
:command_name => set
|
|
128
|
+
:user_id => set[0],
|
|
129
|
+
:system_id => set[1],
|
|
130
|
+
:command_name => set[2]
|
|
134
131
|
)
|
|
135
132
|
summary = summary_jobs.summary(time_range)
|
|
136
133
|
Lock[:job_summaries].synchronize do
|
|
137
134
|
sum = unscoped.find_or_initialize_by(
|
|
138
135
|
:date => date,
|
|
139
|
-
:user_id => set
|
|
140
|
-
:system_id => set
|
|
141
|
-
:command_name => set
|
|
136
|
+
:user_id => set[0],
|
|
137
|
+
:system_id => set[1],
|
|
138
|
+
:command_name => set[2]
|
|
142
139
|
)
|
|
143
140
|
sum.cpu_time = summary[:cpu_time]
|
|
144
141
|
sum.memory_time = summary[:memory_time]
|
|
@@ -147,7 +144,7 @@ module Bookie
|
|
|
147
144
|
end
|
|
148
145
|
end
|
|
149
146
|
end
|
|
150
|
-
|
|
147
|
+
|
|
151
148
|
##
|
|
152
149
|
#Returns a summary of jobs in the database
|
|
153
150
|
#
|
|
@@ -178,7 +175,7 @@ module Bookie
|
|
|
178
175
|
end
|
|
179
176
|
|
|
180
177
|
time_range = time_range.normalized
|
|
181
|
-
|
|
178
|
+
|
|
182
179
|
date_begin = time_range.begin.utc.to_date
|
|
183
180
|
rounded_date_begin = false
|
|
184
181
|
#Round date_begin up.
|
|
@@ -199,7 +196,7 @@ module Bookie
|
|
|
199
196
|
successful = jobs_in_range.where('jobs.exit_code = 0').count
|
|
200
197
|
cpu_time = 0
|
|
201
198
|
memory_time = 0
|
|
202
|
-
|
|
199
|
+
|
|
203
200
|
#To consider: check if num_jobs is zero so we can skip all this?
|
|
204
201
|
if rounded_date_begin
|
|
205
202
|
#We need to get a summary for the chunk up to the first whole day.
|
|
@@ -218,7 +215,7 @@ module Bookie
|
|
|
218
215
|
end
|
|
219
216
|
|
|
220
217
|
date_range = date_begin ... date_end
|
|
221
|
-
|
|
218
|
+
|
|
222
219
|
#Now we can process the cached summaries.
|
|
223
220
|
unscoped = self.unscoped
|
|
224
221
|
summaries = by_date_range(date_range).order(:date).to_a
|
|
@@ -245,7 +242,7 @@ module Bookie
|
|
|
245
242
|
end
|
|
246
243
|
index = new_index
|
|
247
244
|
end
|
|
248
|
-
|
|
245
|
+
|
|
249
246
|
{
|
|
250
247
|
:num_jobs => num_jobs,
|
|
251
248
|
:successful => successful,
|
|
@@ -253,13 +250,13 @@ module Bookie
|
|
|
253
250
|
:memory_time => memory_time,
|
|
254
251
|
}
|
|
255
252
|
end
|
|
256
|
-
|
|
253
|
+
|
|
257
254
|
validates_presence_of :user_id, :system_id, :date, :cpu_time, :memory_time
|
|
258
|
-
|
|
255
|
+
|
|
259
256
|
validates_each :command_name do |record, attr, value|
|
|
260
257
|
record.errors.add(attr, 'must not be nil') if value == nil
|
|
261
258
|
end
|
|
262
|
-
|
|
259
|
+
|
|
263
260
|
validates_each :cpu_time, :memory_time do |record, attr, value|
|
|
264
261
|
record.errors.add(attr, 'must be a non-negative integer') unless value && value >= 0
|
|
265
262
|
end
|