amee 2.0.35 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,11 @@
1
+ = Changelog
2
+
3
+ == 2.2.0
4
+
5
+ * Add SSL support. SSL connections are now used by default.
6
+ * Log4r support
7
+ * Support for reading ItemValueDefinitions
8
+ * Include accessors from other objects
9
+ * Internal improvements including
10
+ * Improved paging support
11
+ * Tidier code for collections
data/README CHANGED
@@ -12,13 +12,15 @@ Documentation: http://docs.github.com/Floppy/amee-ruby
12
12
 
13
13
  == INSTALLATION
14
14
 
15
- 1) Enable gems from gemcutter, if you haven't already done so:
16
- > sudo gem install gemcutter
17
- > sudo gem tumble
18
-
19
- 2) Install gem
15
+ 1) Install gem
20
16
  > sudo gem install amee
21
17
 
18
+ == IMPORTANT CHANGES when upgrading to 2.2.0 and above
19
+
20
+ SSL connections are now supported, and are used BY DEFAULT.If you do not want to
21
+ use SSL, you can disable it using the ":ssl => false" option to Connection.new, or
22
+ by adding "ssl: false" to your amee.yml if you are using Rails.
23
+
22
24
  == IMPORTANT CHANGES when upgrading beyond 2.0.25
23
25
 
24
26
  If you are using the $amee connection in your Rails apps, this is now deprecated
@@ -60,7 +62,7 @@ This gem can also be used as a Rails plugin. You can either extract it into
60
62
  vendor/plugins, or use the new-style config.gem command in environment.rb. For
61
63
  example:
62
64
 
63
- config.gem "amee", :version => '>= 2.0.26'
65
+ config.gem "amee", :version => '~> 2.2.0'
64
66
 
65
67
  If you copy amee.example.yml from the gem source directory to amee.yml in your
66
68
  app's config directory, a persistent AMEE connection will be available from
data/amee.example.yml CHANGED
@@ -2,11 +2,14 @@ development:
2
2
  server: stage.amee.com
3
3
  username: your_amee_username
4
4
  password: your_amee_password
5
+ #ssl: false # Uncomment if you don't want to use SSL
5
6
  test:
6
7
  server: stage.amee.com
7
8
  username: your_amee_username
8
9
  password: your_amee_password
10
+ #ssl: false # Uncomment if you don't want to use SSL
9
11
  production:
10
12
  server: live.amee.com
11
13
  username: your_amee_username
12
14
  password: your_amee_password
15
+ #ssl: false # Uncomment if you don't want to use SSL
data/cacert.pem ADDED
@@ -0,0 +1,45 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUF
3
+ ADCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
4
+ IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw
5
+ HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVU
6
+ Ti1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5
7
+ MTgxOTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQH
8
+ Ew5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3
9
+ b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNV
10
+ BAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3DQEBAQUA
11
+ A4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZ
12
+ FvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6N
13
+ q9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEH
14
+ OG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNdoI6yqqr2jmmI
15
+ BsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjfPe58
16
+ BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhb
17
+ AgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
18
+ VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWG
19
+ M2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3
20
+ YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF
21
+ BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0o
22
+ XnWO6y1n7k57K9cM//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjA
23
+ bPLPSbtNk28GpgoiskliCE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59
24
+ Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4f
25
+ Fm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchq
26
+ J/kniCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0q
27
+ UZ6B+dQ7XnASfxAynB67nfhmqA==
28
+ -----END CERTIFICATE-----
29
+ -----BEGIN CERTIFICATE-----
30
+ MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJV
31
+ UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1
32
+ aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0
33
+ MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoT
34
+ E0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJl
35
+ IEdsb2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
36
+ gYkCgYEAuucXkAJlsTRVPEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQy
37
+ td4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORR
38
+ OhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxnhcXIw2EC
39
+ AwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8w
40
+ HwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6o
41
+ oHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf
42
+ 2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkAZ70Br83gcfxa
43
+ z2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIYNMR1
44
+ pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
45
+ -----END CERTIFICATE-----
data/lib/amee.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rexml/document'
2
2
  require 'active_support'
3
+ require 'log4r'
3
4
 
4
5
  # We don't NEED the JSON gem, but if it's available, use it.
5
6
  begin
@@ -26,9 +27,12 @@ class String
26
27
  end
27
28
  end
28
29
 
30
+ require 'amee/logger'
29
31
  require 'amee/version'
30
32
  require 'amee/exceptions'
31
33
  require 'amee/connection'
34
+ require 'amee/parse_helper'
35
+ require 'amee/collection'
32
36
  require 'amee/object'
33
37
  require 'amee/data_object'
34
38
  require 'amee/profile_object'
@@ -43,6 +47,7 @@ require 'amee/profile_item_value'
43
47
  require 'amee/drill_down'
44
48
  require 'amee/pager'
45
49
  require 'amee/item_definition'
50
+ require 'amee/item_value_definition'
46
51
  require 'amee/user'
47
52
 
48
53
  class Date
@@ -0,0 +1,64 @@
1
+ module AMEE
2
+ class Collection < Array
3
+ include ParseHelper
4
+ attr_reader :pager,:connection,:doc,:json,:response
5
+ def initialize(connection, options = {})
6
+ # Load data from path
7
+ @options=options
8
+ @max=options.delete :resultMax
9
+ @connection=connection
10
+ # Parse data from response
11
+ each_page do
12
+ parse_page
13
+ end
14
+ rescue => err
15
+ #raise AMEE::BadData.new("Couldn't load #{self.class.name}.\n#{response} due to #{err}")
16
+ raise AMEE::BadData.new("Couldn't load #{self.class.name}.\n#{response}")
17
+ end
18
+ def parse_page
19
+ if json
20
+ jsoncollector.each do |p|
21
+ obj = klass.new(parse_json(p))
22
+ obj.connection = connection
23
+ self << obj
24
+ break if @max&&length>=@max
25
+ end
26
+ else
27
+ REXML::XPath.first(doc,xmlcollectorpath.split('/')[1...-1].join('/')) or
28
+ raise AMEE::BadData.new("Couldn't load #{self.class.name}.\n#{response}")
29
+ REXML::XPath.each(doc, xmlcollectorpath) do |p|
30
+ obj=klass.new(parse_xml(p))
31
+ obj.connection = connection
32
+ self << obj
33
+ break if @max&&length>=@max
34
+ end
35
+ end
36
+ end
37
+
38
+ def fetch
39
+ @options.merge! @pager.options if @pager
40
+ @response= @connection.get(collectionpath, @options).body
41
+ if @response.is_json?
42
+ @json = true
43
+ @doc = JSON.parse(@response)
44
+ else
45
+ @doc = REXML::Document.new(@response)
46
+ end
47
+ end
48
+
49
+ def each_page
50
+ begin
51
+ fetch
52
+ yield
53
+ if json
54
+ @pager = AMEE::Pager.from_json(doc['pager'])
55
+ else
56
+ @pager = AMEE::Pager.from_xml(REXML::XPath.first(doc, '/Resources//Pager'))
57
+ end
58
+ break if @max && length>=@max
59
+ end while @pager && @pager.next! #pager is nil if no pager in response,
60
+ # pager.next! is false if @pager said current=last.
61
+ end
62
+ end
63
+ end
64
+
@@ -1,8 +1,11 @@
1
1
  require 'net/http'
2
+ require 'net/https'
2
3
 
3
4
  module AMEE
4
5
  class Connection
5
6
 
7
+ RootCA = File.dirname(__FILE__) + '/../../cacert.pem'
8
+
6
9
  def initialize(server, username, password, options = {})
7
10
  unless options.is_a?(Hash)
8
11
  raise AMEE::ArgumentError.new("Fourth argument must be a hash of options!")
@@ -10,8 +13,11 @@ module AMEE
10
13
  @server = server
11
14
  @username = username
12
15
  @password = password
16
+ @ssl = (options[:ssl] == false) ? false : true
17
+ @port = @ssl ? 443 : 80
13
18
  @auth_token = nil
14
19
  @format = options[:format] || (defined?(JSON) ? :json : :xml)
20
+ @amee_source = options[:amee_source]
15
21
  if !valid?
16
22
  raise "You must supply connection details - server, username and password are all required!"
17
23
  end
@@ -20,20 +26,29 @@ module AMEE
20
26
  $cache ||= {}
21
27
  end
22
28
  # Make connection to server
23
- @http = Net::HTTP.new(@server)
29
+ @http = Net::HTTP.new(@server, @port)
30
+ if @ssl == true
31
+ @http.use_ssl = true
32
+ if File.exists? RootCA
33
+ @http.ca_file = RootCA
34
+ @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
35
+ @http.verify_depth = 5
36
+ end
37
+ end
24
38
  @http.read_timeout = 60
25
39
  @http.set_debug_output($stdout) if options[:enable_debug]
40
+ @debug = options[:enable_debug]
26
41
  end
27
42
 
28
43
  attr_reader :format
29
44
  attr_reader :server
30
45
  attr_reader :username
31
46
  attr_reader :password
32
-
47
+
33
48
  def timeout
34
49
  @http.read_timeout
35
50
  end
36
-
51
+
37
52
  def timeout=(t)
38
53
  @http.read_timeout = t
39
54
  end
@@ -46,7 +61,7 @@ module AMEE
46
61
  def valid?
47
62
  @username && @password && @server
48
63
  end
49
-
64
+
50
65
  def authenticated?
51
66
  !@auth_token.nil?
52
67
  end
@@ -68,7 +83,7 @@ module AMEE
68
83
  $cache[path] = response if @enable_caching
69
84
  return response
70
85
  end
71
-
86
+
72
87
  def post(path, data = {})
73
88
  # Allow format override
74
89
  format = data.delete(:format) || @format
@@ -140,6 +155,7 @@ module AMEE
140
155
  post = Net::HTTP::Post.new("/auth/signIn")
141
156
  post.body = "username=#{@username}&password=#{@password}"
142
157
  post['Accept'] = content_type(:xml)
158
+ post['X-AMEE-Source'] = @amee_source if @amee_source
143
159
  response = @http.request(post)
144
160
  @auth_token = response['authToken']
145
161
  unless authenticated?
@@ -167,7 +183,7 @@ module AMEE
167
183
  return 'application/atom+xml'
168
184
  end
169
185
  end
170
-
186
+
171
187
  def redirect?(response)
172
188
  response.code == '301' || response.code == '302'
173
189
  end
@@ -176,6 +192,8 @@ module AMEE
176
192
  case response.code
177
193
  when '200', '201'
178
194
  return true
195
+ when '404'
196
+ raise AMEE::NotFound.new("The URL was not found on the server.\nRequest: #{request.method} #{request.path}")
179
197
  when '403'
180
198
  raise AMEE::PermissionDenied.new("You do not have permission to perform the requested operation.\nRequest: #{request.method} #{request.path}\n#{request.body}\Response: #{response.body}")
181
199
  when '401'
@@ -197,6 +215,7 @@ module AMEE
197
215
  @http.start
198
216
  # Do request
199
217
  begin
218
+ Logger.log.debug("Requesting #{request.class} at #{request.path} with #{request.body} in format #{format}")
200
219
  response = send_request(request, format)
201
220
  end while !response_ok?(response, request)
202
221
  # Return response
@@ -207,19 +226,17 @@ module AMEE
207
226
  # Close HTTP connection
208
227
  @http.finish if @http.started?
209
228
  end
210
-
229
+
211
230
  def send_request(request, format = @format)
212
231
  # Set auth token in cookie (and header just in case someone's stripping cookies)
213
232
  request['Cookie'] = "authToken=#{@auth_token}"
214
233
  request['authToken'] = @auth_token
215
234
  # Set accept header
216
235
  request['Accept'] = content_type(format)
236
+ # Set AMEE source header if set
237
+ request['X-AMEE-Source'] = @amee_source if @amee_source
217
238
  # Do the business
218
239
  response = @http.request(request)
219
- # Handle 404s
220
- if response.code == '404'
221
- raise AMEE::NotFound.new("URL doesn't exist on server.")
222
- end
223
240
  # Done
224
241
  response
225
242
  end
@@ -17,6 +17,11 @@ module AMEE
17
17
  attr_reader :pager
18
18
  attr_reader :itemdef
19
19
 
20
+ def item_definition
21
+ return nil unless itemdef
22
+ @item_definition ||= AMEE::Admin::ItemDefinition.load(connection,itemdef)
23
+ end
24
+
20
25
  def self.from_json(json)
21
26
  # Parse json
22
27
  doc = JSON.parse(json)
@@ -52,12 +57,15 @@ module AMEE
52
57
  rescue
53
58
  raise AMEE::BadData.new("Couldn't load DataCategory from JSON data. Check that your URL is correct.\n#{json}")
54
59
  end
55
-
60
+
61
+ def self.xmlpathpreamble
62
+ "/Resources/DataCategoryResource/DataCategory/"
63
+ end
56
64
  def self.from_xml(xml)
57
65
  # Parse XML
58
- doc = REXML::Document.new(xml)
66
+ @doc = doc= REXML::Document.new(xml)
59
67
  data = {}
60
- data[:uid] = REXML::XPath.first(doc, "/Resources/DataCategoryResource/DataCategory/@uid").to_s
68
+ data[:uid] = x '@uid'
61
69
  data[:created] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataCategoryResource/DataCategory/@created").to_s)
62
70
  data[:modified] = DateTime.parse(REXML::XPath.first(doc, "/Resources/DataCategoryResource/DataCategory/@modified").to_s)
63
71
  data[:name] = REXML::XPath.first(doc, '/Resources/DataCategoryResource/DataCategory/?ame').text
@@ -90,7 +98,7 @@ module AMEE
90
98
  rescue
91
99
  raise AMEE::BadData.new("Couldn't load DataCategory from XML data. Check that your URL is correct.\n#{xml}")
92
100
  end
93
-
101
+
94
102
  def self.get(connection, path, orig_options = {})
95
103
  unless orig_options.is_a?(Hash)
96
104
  raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
@@ -119,11 +127,11 @@ module AMEE
119
127
  rescue
120
128
  raise AMEE::BadData.new("Couldn't load DataCategory. Check that your URL is correct.\n#{response}")
121
129
  end
122
-
130
+
123
131
  def self.root(connection)
124
132
  self.get(connection, '/data')
125
133
  end
126
-
134
+
127
135
  def child(child_path)
128
136
  AMEE::Data::Category.get(connection, "#{full_path}/#{child_path}")
129
137
  end
@@ -148,7 +156,7 @@ module AMEE
148
156
 
149
157
  connection = category.connection
150
158
  path = category.full_path
151
-
159
+
152
160
  # Do we want to automatically fetch the item afterwards?
153
161
  get_item = options.delete(:get_item)
154
162
 
@@ -162,7 +170,7 @@ module AMEE
162
170
  options[:newObjectType] = "DC"
163
171
  response = connection.post(path, options)
164
172
  if response['Location']
165
- location = response['Location'].match("http://.*?(/.*)")[1]
173
+ location = response['Location'].match("https??://.*?(/.*)")[1]
166
174
  else
167
175
  category = Category.parse(connection, response.body)
168
176
  location = category.full_path
@@ -200,7 +208,7 @@ module AMEE
200
208
  rescue
201
209
  raise AMEE::BadData.new("Couldn't update Data Category. Check that your information is correct.")
202
210
  end
203
-
211
+
204
212
  end
205
213
  end
206
214
  end
@@ -8,21 +8,29 @@ module AMEE
8
8
  @values = data[:values]
9
9
  @choices = data[:choices]
10
10
  @label = data[:label]
11
- @item_definition = data[:item_definition]
11
+ @item_definition_uid = data[:item_definition]
12
12
  @total_amount = data[:total_amount]
13
13
  @total_amount_unit = data[:total_amount_unit]
14
14
  @start_date = data[:start_date]
15
+ @category_uid = data[:category_uid]
15
16
  super
16
17
  end
17
18
 
18
19
  attr_reader :values
19
20
  attr_reader :choices
20
21
  attr_reader :label
21
- attr_reader :item_definition
22
22
  attr_reader :total_amount
23
23
  attr_reader :total_amount_unit
24
24
  attr_reader :start_date
25
-
25
+ attr_reader :category_uid
26
+ attr_reader :item_definition_uid
27
+
28
+ def item_definition
29
+ return nil unless item_definition_uid
30
+ @item_definition ||= AMEE::Admin::ItemDefinition.load(connection,item_definition_uid)
31
+ end
32
+
33
+
26
34
  def self.from_json(json)
27
35
  # Read JSON
28
36
  doc = JSON.parse(json)
@@ -34,6 +42,7 @@ module AMEE
34
42
  data[:path] = doc['path']
35
43
  data[:label] = doc['dataItem']['label']
36
44
  data[:item_definition] = doc['dataItem']['itemDefinition']['uid']
45
+ data[:category_uid] = doc['dataItem']['dataCategory']['uid']
37
46
  # Read v2 total
38
47
  data[:total_amount] = doc['amount']['value'] rescue nil
39
48
  data[:total_amount_unit] = doc['amount']['unit'] rescue nil
@@ -67,7 +76,7 @@ module AMEE
67
76
  rescue
68
77
  raise AMEE::BadData.new("Couldn't load DataItem from JSON. Check that your URL is correct.\n#{json}")
69
78
  end
70
-
79
+
71
80
  def self.from_xml(xml)
72
81
  # Parse data from response into hash
73
82
  doc = REXML::Document.new(xml)
@@ -79,6 +88,7 @@ module AMEE
79
88
  data[:path] = (REXML::XPath.first(doc, '/Resources/DataItemResource/Path') || REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/path')).text
80
89
  data[:label] = (REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/Label') || REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/label')).text
81
90
  data[:item_definition] = REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/ItemDefinition/@uid').to_s
91
+ data[:category_uid] = REXML::XPath.first(doc, '/Resources/DataItemResource/DataItem/DataCategory/@uid').to_s
82
92
  # Read v2 total
83
93
  data[:total_amount] = REXML::XPath.first(doc, '/Resources/DataItemResource/Amount').text.to_f rescue nil
84
94
  data[:total_amount_unit] = REXML::XPath.first(doc, '/Resources/DataItemResource/Amount/@unit').to_s rescue nil
@@ -113,7 +123,7 @@ module AMEE
113
123
  raise AMEE::BadData.new("Couldn't load DataItem from XML. Check that your URL is correct.\n#{xml}")
114
124
  end
115
125
 
116
-
126
+
117
127
  def self.get(connection, path, options = {})
118
128
  # Load data from path
119
129
  response = connection.get(path, options).body
@@ -171,7 +181,7 @@ module AMEE
171
181
  # Send data to path
172
182
  response = connection.post(path, options)
173
183
  if response['Location']
174
- location = response['Location'].match("http://.*?(/.*)")[1]
184
+ location = response['Location'].match("https??://.*?(/.*)")[1]
175
185
  else
176
186
  category = Category.parse(connection, response.body)
177
187
  location = category.full_path + "/" + category.items[0][:path]
@@ -203,7 +213,7 @@ module AMEE
203
213
  rescue
204
214
  raise AMEE::BadData.new("Couldn't update DataItem. Check that your information is correct.\n#{response}")
205
215
  end
206
-
216
+
207
217
  def update(options = {})
208
218
  AMEE::Data::Item.update(connection, full_path, options)
209
219
  end
@@ -213,7 +223,7 @@ module AMEE
213
223
  rescue
214
224
  raise AMEE::BadData.new("Couldn't delete DataItem. Check that your information is correct.")
215
225
  end
216
-
226
+
217
227
  def value(name_or_path_or_uid)
218
228
  val = values.find{ |x| x[:name] == name_or_path_or_uid || x[:path] == name_or_path_or_uid || x[:uid] == name_or_path_or_uid}
219
229
  val ? val[:value] : nil