osm 0.0.11 → 0.0.12

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.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## Version 0.0.12
2
+
3
+ * EveningActivity class renamed to Evening::Activity
4
+ * Change of method return types
5
+ * Activity.badges now returns an array of Osm::Activity::Badge objects
6
+ * Activity.files now returns an array of Osm::Activity::File objects
7
+ * Activity.versions now returns an array of Osm::Activity::Version objects
8
+ * Section.flexi_records now returns an array of Osm::Section::FlexiRecord objects
9
+ * Api.get\_register\_structure now returns an array of RegisterField objects
10
+ * Api.get\_register becomes Api.get\_register\_data and now returns an array of RegisterData objects
11
+ * Attribute name changes:
12
+ * Activity::Badge.section becomes section\_type
13
+ * Activity::File.file\_id becomes id
14
+ * Section.extra\_records becomes flexi\_records
15
+ * Member.joined\_in\_years attribute becomes joining\_in\_years
16
+ * from\_api method added to:
17
+ * Activity and sub classes
18
+ * ApiAccess
19
+ * DueBadges
20
+ * Evening and Evening::Activity
21
+ * Event
22
+ * Grouping
23
+ * Member
24
+ * RegisterData
25
+ * RegisterField
26
+ * Role
27
+ * Section
28
+ * Term
29
+
1
30
  ## Version 0.0.11
2
31
 
3
32
  * Fix undefined variable in id\_for\_term
data/lib/osm.rb CHANGED
@@ -27,7 +27,7 @@ module Osm
27
27
  end
28
28
  end
29
29
 
30
- raise Error.new('There is no current term for the section.')
30
+ raise Error, 'There is no current term for the section.'
31
31
  end
32
32
 
33
33
  def self.make_datetime(date, time)
@@ -69,7 +69,7 @@ module Osm
69
69
  end
70
70
 
71
71
  def self.symbolize_hash(hash_in)
72
- raise ArgumentError.new('You did not pass in a hash') unless hash_in.is_a?(Hash)
72
+ raise ArgumentError, 'You did not pass in a hash' unless hash_in.is_a?(Hash)
73
73
 
74
74
  hash_out = {}
75
75
  hash_in.each do |key, value|
@@ -78,4 +78,12 @@ module Osm
78
78
  hash_out
79
79
  end
80
80
 
81
+ def self.is_array_of?(ar, ty)
82
+ return false unless ar.is_a?(Array)
83
+ ar.each do |it|
84
+ return false unless it.is_a?(ty)
85
+ end
86
+ return true
87
+ end
88
+
81
89
  end
data/lib/osm/activity.rb CHANGED
@@ -34,53 +34,209 @@ module Osm
34
34
  # @!attribute [r] used
35
35
  # @return [Fixnum] How many times this activity has been used (total accross all of OSM)
36
36
  # @!attribute [r] versions
37
- # @return [Array<Hash>] ? (:value - version, :firstname - created by, :label - label, :user_id - OSM user ID of creator)
37
+ # @return [Array<Osm::Activity::Version>]
38
38
  # @!attribute [r] sections
39
39
  # @return [Array<Symbol>] the sections the activity is appropriate for
40
40
  # @!attribute [r] tags
41
41
  # @return [Array<String>] the tags attached to the activity
42
42
  # @!attribute [r] files
43
- # @return [Array<Hash> ? ('fileid', 'filename', 'name')
43
+ # @return [Array<Osm::Activity::File>
44
44
  # @!attribute [r] badges
45
- # @return [Array<Hash> ? ('section', 'badgetype', 'badge', 'columnname', 'label')
46
-
47
-
48
- # Initialize a new Activity using the hash returned by the API call
49
- # @param data the hash of data for the object returned by the API
50
- def initialize(data)
51
- @id = data['details']['activityid'].to_i
52
- @version = data['details']['version'].to_i
53
- @group_id = data['details']['groupid'].to_i
54
- @user_id = data['details']['userid'].to_i
55
- @title = data['details']['title']
56
- @description = data['details']['description']
57
- @resources = data['details']['resources']
58
- @instructions = data['details']['instructions']
59
- @running_time = data['details']['runningtime'].to_i
60
- @location = data['details']['location'].to_sym
61
- @shared = data['details']['shared'].to_i
62
- @rating = data['details']['rating'].to_i
63
- @editable = data['editable']
64
- @deletable = data['deletable'] ? true : false
65
- @used = data['used'].to_i
66
- @versions = data['versions']
67
- @sections = data['sections'].is_a?(Array) ? Osm::make_array_of_symbols(data['sections']) : []
68
- @tags = data['tags'].is_a?(Array) ? data['tags'] : []
69
- @files = data['files'].is_a?(Array) ? data['files'] : []
70
- @badges = data['badges'].is_a?(Array) ? data['badges'] : []
71
-
72
- # Clean versions hashes
73
- @versions.each do |version|
74
- version.keys.each do |key|
75
- version[(key.to_sym rescue key) || key] = version.delete(key)
76
- end
77
- version[:value] = version[:value].to_i
78
- version[:user_id] = version[:userid].to_i
79
- version.delete(:userid)
80
- version[:selected] = (version[:selected] == 'selected')
45
+ # @return [Array<Osm::Activity::Badge>
46
+
47
+
48
+ # Initialize a new Activity
49
+ # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
50
+ def initialize(attributes={})
51
+ [:id, :group_id, :user_id].each do |attribute|
52
+ it = attributes[attribute]
53
+ raise ArgumentError, ":#{attribute} must be nil or a Fixnum > 0" unless it.nil? || (it.is_a?(Fixnum) && it > 0)
54
+ end
55
+ [:version, :running_time, :shared].each do |attribute|
56
+ it = attributes[attribute]
57
+ raise ArgumentError, ":#{attribute} must be nil or a Fixnum >= 0" unless it.nil? || (it.is_a?(Fixnum) && it >= 0)
58
+ end
59
+
60
+ [:title, :description, :resources, :instructions].each do |attribute|
61
+ it = attributes[attribute]
62
+ raise ArgumentError, ":#{attribute} must be nil or a String" unless it.nil? || it.is_a?(String)
63
+ end
64
+
65
+ raise ArgumentError, ':location must be either :indoors, :outdoors or :both' unless [:indoors, :outdoors, :both].include?(attributes[:location])
66
+
67
+ raise ArgumentError, ':editable must be a Boolean' unless attributes[:editable].is_a?(TrueClass) || attributes[:editable].is_a?(FalseClass)
68
+ raise ArgumentError, ':deletable must be a Boolean' unless attributes[:deletable].is_a?(TrueClass) || attributes[:deletable].is_a?(FalseClass)
69
+
70
+ raise ArgumentError, ':rating must be a FixNum' unless attributes[:rating].is_a?(Fixnum)
71
+ raise ArgumentError, ':used must be a FixNum' unless attributes[:used].is_a?(Fixnum)
72
+
73
+ raise ArgumentError, ':sections must be an Array of Symbol' unless Osm::is_array_of?(attributes[:sections], Symbol)
74
+ raise ArgumentError, ':tags must be an Array of String' unless Osm::is_array_of?(attributes[:tags], String)
75
+ raise ArgumentError, ':versions must be an Array of Osm::Activity::Version' unless Osm::is_array_of?(attributes[:versions], Osm::Activity::Version)
76
+ raise ArgumentError, ':files must be an Array of Osm::Activity::File' unless Osm::is_array_of?(attributes[:files], Osm::Activity::File)
77
+ raise ArgumentError, ':badges must be an Array of Osm::Activity::Badge' unless Osm::is_array_of?(attributes[:badges], Osm::Activity::Badge)
78
+
79
+ attributes.each { |k,v| instance_variable_set("@#{k}", v) }
80
+ end
81
+
82
+ # Initialize a new Activity from api data
83
+ # @param [Hash] data the hash of data provided by the API
84
+ def self.from_api(data)
85
+ attributes = {}
86
+ attributes[:id] = Osm::to_i_or_nil(data['details']['activityid'])
87
+ attributes[:version] = data['details']['version'].to_i
88
+ attributes[:group_id] = Osm::to_i_or_nil(data['details']['groupid'])
89
+ attributes[:user_id] = Osm::to_i_or_nil(data['details']['userid'])
90
+ attributes[:title] = data['details']['title']
91
+ attributes[:description] = data['details']['description']
92
+ attributes[:resources] = data['details']['resources']
93
+ attributes[:instructions] = data['details']['instructions']
94
+ attributes[:running_time] = Osm::to_i_or_nil(data['details']['runningtime'])
95
+ attributes[:location] = data['details']['location'].to_sym
96
+ attributes[:shared] = Osm::to_i_or_nil(data['details']['shared'])
97
+ attributes[:rating] = data['details']['rating'].to_i
98
+ attributes[:editable] = data['editable']
99
+ attributes[:deletable] = data['deletable'] ? true : false
100
+ attributes[:used] = data['used'].to_i
101
+ attributes[:sections] = data['sections'].is_a?(Array) ? Osm::make_array_of_symbols(data['sections']) : []
102
+ attributes[:tags] = data['tags'].is_a?(Array) ? data['tags'] : []
103
+ attributes[:versions] = []
104
+ attributes[:files] = []
105
+ attributes[:badges] = []
106
+
107
+ # Populate Arrays
108
+ (data['files'].is_a?(Array) ? data['files'] : []).each do |file_data|
109
+ attributes[:files].push File.from_api(file_data)
110
+ end
111
+ (data['badges'].is_a?(Array) ? data['badges'] : []).each do |badge_data|
112
+ attributes[:badges].push Badge.from_api(badge_data)
113
+ end
114
+ (data['versions'].is_a?(Array) ? data['versions'] : []).each do |version_data|
115
+ attributes[:versions].push Version.from_api(version_data)
81
116
  end
117
+
118
+ return new(attributes)
82
119
  end
83
120
 
84
- end
85
121
 
86
- end
122
+ private
123
+ class File
124
+ attr_reader :id, :activity_id, :file_name, :name
125
+ # @!attribute [r] id
126
+ # @return [Fixnum] the OSM ID for the file
127
+ # @!attribute [r] activity_id
128
+ # @return [Fixnum] the OSM ID for the activity
129
+ # @!attribute [r] file_name
130
+ # @return [String] the file name of the file
131
+ # @!attribute [r] name
132
+ # @return [String] the name of the file (more human readable than file_name)
133
+
134
+ # Initialize a new File
135
+ # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
136
+ def initialize(attributes={})
137
+ [:file_name, :name].each do |attribute|
138
+ raise ArgumentError, ":#{attribute} must a String" unless attributes[attribute].is_a?(String)
139
+ end
140
+ [:id, :activity_id].each do |attribute|
141
+ it = attributes[attribute]
142
+ raise ArgumentError, ":#{attribute} must be nil or a Fixnum > 0" unless it.nil? || (it.is_a?(Fixnum) && it > 0)
143
+ end
144
+
145
+ attributes.each { |k,v| instance_variable_set("@#{k}", v) }
146
+ end
147
+
148
+ # Initialize a new File from api data
149
+ # @param [Hash] data the hash of data provided by the API
150
+ def self.from_api(data)
151
+ return new({
152
+ :id => Osm::to_i_or_nil(data['fileid']),
153
+ :activity_id => Osm::to_i_or_nil(data['activityid']),
154
+ :file_name => data['filename'],
155
+ :name => data['name']
156
+ })
157
+ end
158
+
159
+ end # Activity::File
160
+
161
+ class Badge
162
+ attr_reader :activity_id, :section_type, :type, :badge, :requirement, :label
163
+ # @!attribute [r] activity_id
164
+ # @return [Fixnum] the activity being done
165
+ # @!attribute [r] section_type
166
+ # @return [Symbol] the section the badge 'belongs' to
167
+ # @!attribute [r] type
168
+ # @return [Symbol] the type of badge
169
+ # @!attribute [r] badge
170
+ # @return [String] short name of the badge
171
+ # @!attribute [r] requirement
172
+ # @return [String] OSM reference to this badge requirement
173
+ # @!attribute [r] label
174
+ # @return [String] human readable label for the requirement
175
+
176
+ # Initialize a new Badge
177
+ # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
178
+ def initialize(attributes={})
179
+ raise ArgumentError, ':activity_id must be nil or a Fixnum > 0' unless attributes[:activity_id].nil? || (attributes[:activity_id].is_a?(Fixnum) && attributes[:activity_id] > 0)
180
+ [:type, :section_type].each do |attribute|
181
+ raise ArgumentError, ":#{attribute} must be a Symbol" unless attributes[attribute].is_a?(Symbol)
182
+ end
183
+ [:label, :requirement, :badge].each do |attribute|
184
+ raise ArgumentError, ":#{attribute} must a String" unless attributes[attribute].is_a?(String)
185
+ end
186
+
187
+ attributes.each { |k,v| instance_variable_set("@#{k}", v) }
188
+ end
189
+
190
+ # Initialize a new Badge from api data
191
+ # @param [Hash] data the hash of data provided by the API
192
+ def self.from_api(data)
193
+ return new({
194
+ :activity_id => Osm::to_i_or_nil(data['activityid']),
195
+ :section_type => data['section'].to_sym,
196
+ :type => data['badgetype'].to_sym,
197
+ :badge => data['badge'],
198
+ :requirement => data['columnname'],
199
+ :label => data['label']
200
+ })
201
+ end
202
+
203
+ end # Activity::Badge
204
+
205
+ class Version
206
+ attr_reader :version, :created_by, :created_by_name, :label
207
+ # @!attribute [r] version
208
+ # @return [Fixnum] the version of the activity
209
+ # @!attribute [r] created_by
210
+ # @return [Fixnum] the OSM user ID of the person who created this version
211
+ # @!attribute [r] created_by_name
212
+ # @return [String] the aname of the OSM user who created this version
213
+ # @!attribute [r] label
214
+ # @return [String] the human readable label to use for this version
215
+
216
+ # Initialize a new Version
217
+ # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
218
+ def initialize(attributes={})
219
+ raise ArgumentError, ':version must be nil or a Fixnum > 0' unless attributes[:version].nil? || (attributes[:version].is_a?(Fixnum) && attributes[:version] >= 0)
220
+ raise ArgumentError, ':created_by must be nil or a Fixnum >= 0' unless attributes[:created_by].nil? || (attributes[:created_by].is_a?(Fixnum) && attributes[:created_by] > 0)
221
+ raise ArgumentError, ':created_by_name must be nil or a String' unless attributes[:created_by_name].nil? || attributes[:created_by_name].is_a?(String)
222
+ raise ArgumentError, ':label must be nil or a String' unless attributes[:label].nil? || attributes[:label].is_a?(String)
223
+
224
+ attributes.each { |k,v| instance_variable_set("@#{k}", v) }
225
+ end
226
+
227
+ # Initialize a new Version from api data
228
+ # @param [Hash] data the hash of data provided by the API
229
+ def self.from_api(data)
230
+ return new({
231
+ :version => Osm::to_i_or_nil(data['value']),
232
+ :created_by => Osm::to_i_or_nil(data['userid']),
233
+ :created_by_name => data['firstname'],
234
+ :label => data['label']
235
+ })
236
+ end
237
+
238
+ end # Activity::Version
239
+
240
+ end # Activity
241
+
242
+ end # Module
data/lib/osm/api.rb CHANGED
@@ -113,7 +113,7 @@ module Osm
113
113
 
114
114
  result = Array.new
115
115
  data.each do |item|
116
- role = Osm::Role.new(item)
116
+ role = Osm::Role.from_api(item)
117
117
  result.push role
118
118
  cache_write("section-#{role.section.id}", role.section, :expires_in => @@default_cache_ttl*2)
119
119
  self.user_can_access :section, role.section.id, api_data
@@ -205,7 +205,7 @@ module Osm
205
205
 
206
206
  result = Array.new
207
207
  data['patrols'].each do |item|
208
- grouping = Osm::Grouping.new(item)
208
+ grouping = Osm::Grouping.from_api(item)
209
209
  result.push grouping
210
210
  cache_write("grouping-#{grouping.id}", grouping, :expires_in => @@default_cache_ttl*2)
211
211
  self.user_can_access :grouping, grouping.id, api_data
@@ -229,7 +229,7 @@ module Osm
229
229
  result = Array.new
230
230
  data.each_key do |key|
231
231
  data[key].each do |item|
232
- term = Osm::Term.new(item)
232
+ term = Osm::Term.from_api(item)
233
233
  result.push term
234
234
  cache_write("term-#{term.id}", term, :expires_in => @@default_cache_ttl*2)
235
235
  self.user_can_access :term, term.id, api_data
@@ -284,7 +284,7 @@ module Osm
284
284
  activities = data['activities'] || {}
285
285
 
286
286
  items.each do |item|
287
- evening = Osm::Evening.new(item, activities[item['eveningid']])
287
+ evening = Osm::Evening.from_api(item, activities[item['eveningid']])
288
288
  result.push evening
289
289
  evening.activities.each do |activity|
290
290
  self.user_can_access :activity, activity.activity_id, api_data
@@ -313,7 +313,7 @@ module Osm
313
313
  data = perform_query("programme.php?action=getActivity&id=#{activity_id}&version=#{version}", api_data)
314
314
  end
315
315
 
316
- activity = Osm::Activity.new(data)
316
+ activity = Osm::Activity.from_api(data)
317
317
  cache_write("activity-#{activity_id}-#{nil}", activity, :expires_in => @@default_cache_ttl*2) if version.nil?
318
318
  cache_write("activity-#{activity_id}-#{activity.version}", activity, :expires_in => @@default_cache_ttl/2)
319
319
  self.user_can_access :activity, activity.id, api_data
@@ -339,7 +339,7 @@ module Osm
339
339
 
340
340
  result = Array.new
341
341
  data['items'].each do |item|
342
- result.push Osm::Member.new(item)
342
+ result.push Osm::Member.from_api(item)
343
343
  end
344
344
  self.user_can_access :member, section_id, api_data
345
345
  cache_write("members-#{section_id}-#{term_id}", result, :expires_in => @@default_cache_ttl)
@@ -363,7 +363,7 @@ module Osm
363
363
 
364
364
  result = Array.new
365
365
  data['apis'].each do |item|
366
- this_item = Osm::ApiAccess.new(item)
366
+ this_item = Osm::ApiAccess.from_api(item)
367
367
  result.push this_item
368
368
  self.user_can_access(:programme, section_id, api_data) if this_item.can_read?(:programme)
369
369
  self.user_can_access(:member, section_id, api_data) if this_item.can_read?(:member)
@@ -412,7 +412,7 @@ module Osm
412
412
  result = Array.new
413
413
  unless data['items'].nil?
414
414
  data['items'].each do |item|
415
- result.push Osm::Event.new(item)
415
+ result.push Osm::Event.from_api(item)
416
416
  end
417
417
  end
418
418
  self.user_can_access :programme, section_id, api_data
@@ -437,7 +437,7 @@ module Osm
437
437
  section_type = get_section(section_id, api_data).type.to_s
438
438
  data = perform_query("challenges.php?action=outstandingBadges&section=#{section_type}&sectionid=#{section_id}&termid=#{term_id}", api_data)
439
439
 
440
- data = Osm::DueBadges.new(data)
440
+ data = Osm::DueBadges.from_api(data)
441
441
  self.user_can_access :badge, section_id, api_data
442
442
  cache_write("due_badges-#{section_id}-#{term_id}", data, :expires_in => @@default_cache_ttl*2)
443
443
 
@@ -449,7 +449,7 @@ module Osm
449
449
  # @param [Osm:Term, Fixnum] section the term (or its ID) to get the structure for, passing nil causes the current term to be used
450
450
  # @!macro options_get
451
451
  # @!macro options_api_data
452
- # @return [Array<Hash>] representing the rows of the register
452
+ # @return [Array<Osm::RegisterField>] representing the fields of the register
453
453
  def get_register_structure(section, term=nil, options={}, api_data={})
454
454
  section_id = id_for_section(section)
455
455
  term_id = id_for_term(term, section, api_data)
@@ -460,25 +460,25 @@ module Osm
460
460
 
461
461
  data = perform_query("users.php?action=registerStructure&sectionid=#{section_id}&termid=#{term_id}", api_data)
462
462
 
463
- data.each_with_index do |item, item_index|
464
- data[item_index] = item = Osm::symbolize_hash(item)
465
- item[:rows].each_with_index do |row, row_index|
466
- item[:rows][row_index] = row = Osm::symbolize_hash(row)
463
+ structure = []
464
+ data.each do |item|
465
+ item['rows'].each do |row|
466
+ structure.push Osm::RegisterField.from_api(row)
467
467
  end
468
468
  end
469
469
  self.user_can_access :register, section_id, api_data
470
- cache_write("register_structure-#{section_id}-#{term_id}", data, :expires_in => @@default_cache_ttl/2)
470
+ cache_write("register_structure-#{section_id}-#{term_id}", structure, :expires_in => @@default_cache_ttl/2)
471
471
 
472
- return data
472
+ return structure
473
473
  end
474
474
 
475
- # Get register
475
+ # Get register data
476
476
  # @param [Osm:Section, Fixnum] section the section (or its ID) to get the register for
477
477
  # @param [Osm:Term, Fixnum] section the term (or its ID) to get the register for, passing nil causes the current term to be used
478
478
  # @!macro options_get
479
479
  # @!macro options_api_data
480
- # @return [Array<Hash>] representing the attendance of each member
481
- def get_register(section, term=nil, options={}, api_data={})
480
+ # @return [Array<RegisterData>] representing the attendance of each member
481
+ def get_register_data(section, term=nil, options={}, api_data={})
482
482
  section_id = id_for_section(section)
483
483
  term_id = id_for_term(term, section, api_data)
484
484
 
@@ -490,10 +490,7 @@ module Osm
490
490
 
491
491
  data = data['items']
492
492
  data.each do |item|
493
- item = Osm::symbolize_hash(item)
494
- item[:scoutid] = item[:scoutid].to_i
495
- item[:sectionid] = item[:sectionid].to_i
496
- item[:patrolid] = item[:patrolid].to_i
493
+ item = Osm::RegisterData.from_api(item)
497
494
  end
498
495
  self.user_can_access :register, section_id, api_data
499
496
  cache_write("register-#{section_id}-#{term_id}", data, :expires_in => @@default_cache_ttl/2)
@@ -528,7 +525,7 @@ module Osm
528
525
  # @!macro options_api_data
529
526
  # @return [Boolean] if the operation suceeded or not
530
527
  def update_evening(evening, api_data={})
531
- response = perform_query("programme.php?action=editEvening", api_data.merge(evening.data_for_saving))
528
+ response = perform_query("programme.php?action=editEvening", api_data.merge(evening.to_api))
532
529
 
533
530
  # The cached programmes for the section will be out of date - remove them
534
531
  get_terms(api_data).each do |term|
@@ -601,14 +598,14 @@ module Osm
601
598
  begin
602
599
  result = HTTParty.post("#{@base_url}/#{url}", {:body => api_data})
603
600
  rescue SocketError, TimeoutError, OpenSSL::SSL::SSLError
604
- raise ConnectionError.new('A problem occured on the internet.')
601
+ raise ConnectionError, 'A problem occured on the internet.'
605
602
  end
606
- raise ConnectionError.new("HTTP Status code was #{result.response.code}") if !result.response.code.eql?('200')
603
+ raise ConnectionError, "HTTP Status code was #{result.response.code}" if !result.response.code.eql?('200')
607
604
 
608
- raise Error.new(result.response.body) unless looks_like_json?(result.response.body)
605
+ raise Error, result.response.body unless looks_like_json?(result.response.body)
609
606
  decoded = ActiveSupport::JSON.decode(result.response.body)
610
607
  osm_error = get_osm_error(decoded)
611
- raise Error.new(osm_error) if osm_error
608
+ raise Error, osm_error if osm_error
612
609
  return decoded
613
610
  end
614
611
 
@@ -654,10 +651,10 @@ module Osm
654
651
  if value.is_a?(cl)
655
652
  value = value.send(id_method)
656
653
  else
657
- raise(ArgumentError, "Invalid type for #{error_name}") unless value.is_a?(Fixnum)
654
+ raise ArgumentError, "Invalid type for #{error_name}" unless value.is_a?(Fixnum)
658
655
  end
659
656
 
660
- raise(ArgumentError, "Invalid #{error_name} ID") unless value > 0
657
+ raise ArgumentError, "Invalid #{error_name} ID" unless value > 0
661
658
  return value
662
659
  end
663
660