amee 2.4.0 → 2.5.0

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  = Changelog
2
2
 
3
+ == 2.5.0
4
+ * Use ActiveSupport::Cache for caching
5
+ * Add ability to specify cache and debug options in amee.yml for Rails projects.
6
+ * Uses Nokogiri for some XML parsing for big speed improvements. More to come.
7
+
8
+ == 2.4.0
9
+ * Collection objects support filter blocks.
10
+
11
+ == 2.3.1
12
+ * Remove required environment UID in AMEE::Admin classes.
13
+
3
14
  == 2.3.0
4
15
  * Remove misleading ItemValueDefinition#type function. Use profile?, data? and
5
16
  drill? separately instead.
data/README CHANGED
@@ -15,6 +15,12 @@ Documentation: http://docs.github.com/Floppy/amee-ruby
15
15
  1) Install gem
16
16
  > sudo gem install amee
17
17
 
18
+ == REQUIREMENTS
19
+
20
+ 'Nokogiri' is used for XML parsing, and requires libxml2. See
21
+ http://nokogiri.org/tutorials/installing_nokogiri.html for instructions if you
22
+ have problems installing.
23
+
18
24
  == IMPORTANT CHANGES when upgrading to 2.2.0 and above
19
25
 
20
26
  SSL connections are now supported, and are used BY DEFAULT.If you do not want to
@@ -86,11 +92,20 @@ AMEE when your model is saved.
86
92
 
87
93
  == CACHING
88
94
 
89
- The AMEE::Connection object implements an optional cache for GET requests. This is
90
- currently a very simple implementation which caches the result of all GET requests
91
- until a POST, PUT, or DELETE is executed, at which point the cache is cleared. To
92
- enable caching, set the enable_caching parameter of AMEE::Connection.new to true.
93
- Caching is disabled by default.
95
+ The AMEE::Connection object implements an optional cache for GET requests.
96
+
97
+ This uses ActiveSupport::Cache, and supports any ActiveSupport::Cache::Store
98
+ caching method except for MemCacheStore (which doesn't provide required features).
99
+
100
+ All GET requests are cached. POST, PUT, or DELETEs will clear the cache for any
101
+ path that matches the affected URL. #expire, #expire_matching, and #expire_all
102
+ functions are available for manual cache management. Also, all AMEE objects have
103
+ and #expire-cache function to clear them and their descendants from the cache.
104
+
105
+ To enable caching, pass ':cache => :memory_store' to AMEE::Connection.new, or add
106
+ 'cache: memory_store' to your amee.yml if using Rails. If you want to use the
107
+ same caching configuration as your Rails app, you can add 'cache: rails' to
108
+ amee.yml instead. Caching is disabled by default.
94
109
 
95
110
  == UPGRADING TO VERSION > 2
96
111
 
@@ -1,4 +1,5 @@
1
1
  require 'rexml/document'
2
+ require 'nokogiri'
2
3
  require 'active_support'
3
4
  require 'log4r'
4
5
 
@@ -25,9 +25,9 @@ module AMEE
25
25
  break if @max&&length>=@max
26
26
  end
27
27
  else
28
- REXML::XPath.first(doc,xmlcollectorpath.split('/')[1...-1].join('/')) or
29
- raise AMEE::BadData.new("Couldn't load #{self.class.name}.\n#{response}")
30
- REXML::XPath.each(doc, xmlcollectorpath) do |p|
28
+ doc.xpath(xmlcollectorpath.split('/')[1...-1].join('/')).first or
29
+ raise AMEE::BadData.new("Couldn't load #{self.class.name}. parp\n#{response}")
30
+ doc.xpath(xmlcollectorpath).each do |p|
31
31
  obj=klass.new(parse_xml(p))
32
32
  obj.connection = connection
33
33
  x= @filter ? @filter.call(obj) : obj
@@ -44,7 +44,7 @@ module AMEE
44
44
  @json = true
45
45
  @doc = JSON.parse(@response)
46
46
  else
47
- @doc = REXML::Document.new(@response)
47
+ @doc = load_xml_doc(@response)
48
48
  end
49
49
  end
50
50
 
@@ -55,7 +55,7 @@ module AMEE
55
55
  if json
56
56
  @pager = AMEE::Pager.from_json(doc['pager'])
57
57
  else
58
- @pager = AMEE::Pager.from_xml(REXML::XPath.first(doc, '/Resources//Pager'))
58
+ @pager = AMEE::Pager.from_xml(doc.xpath('/Resources//Pager').first)
59
59
  end
60
60
  break if @max && length>=@max
61
61
  end while @pager && @pager.next! #pager is nil if no pager in response,
@@ -21,9 +21,26 @@ module AMEE
21
21
  if !valid?
22
22
  raise "You must supply connection details - server, username and password are all required!"
23
23
  end
24
- @enable_caching = options[:enable_caching]
25
- if @enable_caching
26
- $cache ||= {}
24
+ # Handle old option
25
+ if options[:enable_caching]
26
+ Kernel.warn '[DEPRECATED] :enable_caching => true is deprecated. Use :cache => :memory_store instead'
27
+ options[:cache] ||= :memory_store
28
+ end
29
+ # Create cache store
30
+ if options[:cache] &&
31
+ (options[:cache_store].is_a?(ActiveSupport::Cache::MemCacheStore) ||
32
+ options[:cache].to_sym == :mem_cache_store)
33
+ raise 'ActiveSupport::Cache::MemCacheStore is not supported, as it doesn\'t allow regexp expiry'
34
+ end
35
+ if options[:cache_store].is_a?(ActiveSupport::Cache::Store)
36
+ # Allows assignment of the entire cache store in Rails apps
37
+ @cache = options[:cache_store]
38
+ elsif options[:cache]
39
+ if options[:cache_options]
40
+ @cache = ActiveSupport::Cache.lookup_store(options[:cache].to_sym, options[:cache_options])
41
+ else
42
+ @cache = ActiveSupport::Cache.lookup_store(options[:cache].to_sym)
43
+ end
27
44
  end
28
45
  # Make connection to server
29
46
  @http = Net::HTTP.new(@server, @port)
@@ -77,18 +94,15 @@ module AMEE
77
94
  if params.size > 0
78
95
  path += "?#{params.join('&')}"
79
96
  end
80
- # Send request
81
- return $cache[path] if @enable_caching and $cache[path]
82
- response = do_request(Net::HTTP::Get.new(path), format)
83
- $cache[path] = response if @enable_caching
84
- return response
97
+ # Send request
98
+ cache(path) { do_request(Net::HTTP::Get.new(path), format) }
85
99
  end
86
100
 
87
101
  def post(path, data = {})
88
102
  # Allow format override
89
103
  format = data.delete(:format) || @format
90
104
  # Clear cache
91
- clear_cache
105
+ expire_matching "#{raw_path(path)}.*"
92
106
  # Create POST request
93
107
  post = Net::HTTP::Post.new(path)
94
108
  body = []
@@ -104,7 +118,7 @@ module AMEE
104
118
  # Allow format override
105
119
  format = options.delete(:format) || @format
106
120
  # Clear cache
107
- clear_cache
121
+ expire_matching "#{raw_path(path)}.*"
108
122
  # Create POST request
109
123
  post = Net::HTTP::Post.new(path)
110
124
  post['Content-type'] = options[:content_type] || content_type(format)
@@ -117,7 +131,7 @@ module AMEE
117
131
  # Allow format override
118
132
  format = data.delete(:format) || @format
119
133
  # Clear cache
120
- clear_cache
134
+ expire_matching "#{parent_path(path)}.*"
121
135
  # Create PUT request
122
136
  put = Net::HTTP::Put.new(path)
123
137
  body = []
@@ -133,7 +147,7 @@ module AMEE
133
147
  # Allow format override
134
148
  format = options.delete(:format) || @format
135
149
  # Clear cache
136
- clear_cache
150
+ expire_matching "#{parent_path(path)}.*"
137
151
  # Create PUT request
138
152
  put = Net::HTTP::Put.new(path)
139
153
  put['Content-type'] = options[:content_type] || content_type(format)
@@ -143,7 +157,7 @@ module AMEE
143
157
  end
144
158
 
145
159
  def delete(path)
146
- clear_cache
160
+ expire_matching "#{parent_path(path)}.*"
147
161
  # Create DELETE request
148
162
  delete = Net::HTTP::Delete.new(path)
149
163
  # Send request
@@ -242,12 +256,45 @@ module AMEE
242
256
  response
243
257
  end
244
258
 
259
+ def cache(path, &block)
260
+ key = cache_key(path)
261
+ if @cache && @cache.exist?(key)
262
+ puts "CACHE HIT on #{key}" if @debug
263
+ return @cache.read(key)
264
+ end
265
+ puts "CACHE MISS on #{key}" if @debug
266
+ data = block.call
267
+ @cache.write(key, data) if @cache
268
+ return data
269
+ end
270
+
271
+ def parent_path(path)
272
+ path.split('/')[0..-2].join('/')
273
+ end
274
+
275
+ def raw_path(path)
276
+ path.split(/[;?]/)[0]
277
+ end
278
+
279
+ def cache_key(path)
280
+ # We have to make sure cache keys don't get too long for the filesystem,
281
+ # so we cut them off if they're too long and add a digest for uniqueness.
282
+ newpath = (path.length < 255) ? path : path.first(192)+Digest::MD5.hexdigest(path)
283
+ (@server+newpath)
284
+ end
285
+
245
286
  public
246
287
 
247
- def clear_cache
248
- if @enable_caching
249
- $cache = {}
250
- end
288
+ def expire(path, options = nil)
289
+ @cache.delete(cache_key(path), options) if @cache
290
+ end
291
+
292
+ def expire_matching(matcher, options = nil)
293
+ @cache.delete_matched(Regexp.new(cache_key(matcher)), options) if @cache
294
+ end
295
+
296
+ def expire_all
297
+ @cache.clear if @cache
251
298
  end
252
299
 
253
300
  end
@@ -69,24 +69,29 @@ module AMEE
69
69
  raise AMEE::BadData.new("Couldn't load DataItemValue from JSON. Check that your URL is correct.\n#{json}")
70
70
  end
71
71
 
72
+ def self.xmlpathpreamble
73
+ "descendant-or-self::ItemValue/"
74
+ end
75
+
72
76
  def self.from_xml(xml, path)
73
77
  # Read XML
74
- doc = xml.is_a?(String) ? REXML::Document.new(xml) : xml
78
+ @doc = xml.is_a?(String) ? load_xml_doc(xml) : xml
75
79
  data = {}
76
- if REXML::XPath.match(doc,"descendant-or-self::ItemValue").length>1
80
+ if @doc.xpath("descendant-or-self::ItemValue").length>1
77
81
  raise AMEE::BadData.new("Couldn't load DataItemValue from XML. This is an item value history.\n#{xml}")
78
82
  end
83
+ raise if @doc.xpath("descendant-or-self::ItemValue").length==0
79
84
  begin
80
- data[:uid] = REXML::XPath.first(doc, "descendant-or-self::ItemValue/@uid").to_s
81
- data[:created] = DateTime.parse(REXML::XPath.first(doc, "descendant-or-self::ItemValue/@Created").to_s) rescue nil
82
- data[:modified] = DateTime.parse(REXML::XPath.first(doc, "descendant-or-self::ItemValue/@Modified").to_s) rescue nil
83
- data[:name] = REXML::XPath.first(doc, 'descendant-or-self::ItemValue/Name').text
85
+ data[:uid] = x "@uid"
86
+ data[:created] = DateTime.parse(x "@Created") rescue nil
87
+ data[:modified] = DateTime.parse(x "@Modified") rescue nil
88
+ data[:name] = x 'Name'
84
89
  data[:path] = path.gsub(/^\/data/, '')
85
- data[:value] = REXML::XPath.first(doc, 'descendant-or-self::ItemValue/Value').text
86
- data[:type] = REXML::XPath.first(doc, 'descendant-or-self::ItemValue/ItemValueDefinition/ValueDefinition/ValueType').text
90
+ data[:value] = x 'Value'
91
+ data[:type] = x 'ItemValueDefinition/ValueDefinition/ValueType'
87
92
  data[:from_profile] = false
88
93
  data[:from_data] = true
89
- data[:start_date] = DateTime.parse(REXML::XPath.first(doc, "descendant-or-self::ItemValue/StartDate").text) rescue nil
94
+ data[:start_date] = DateTime.parse(x "StartDate") rescue nil
90
95
  # Create object
91
96
  ItemValue.new(data)
92
97
  rescue
@@ -3,6 +3,7 @@ require 'set'
3
3
  module AMEE
4
4
  module Data
5
5
  class ItemValueHistory
6
+ extend ParseHelper
6
7
 
7
8
  def initialize(data = {})
8
9
  @type = data ? data[:type] : nil
@@ -84,8 +85,8 @@ module AMEE
84
85
  # Read XML
85
86
  data = {}
86
87
  data[:path] = path.gsub(/^\/data/, '')
87
- doc = REXML::Document.new(xml)
88
- valuedocs=REXML::XPath.match(doc, '//ItemValue')
88
+ doc = load_xml_doc(xml)
89
+ valuedocs=doc.xpath('//ItemValue')
89
90
  raise if valuedocs.length==0
90
91
  data[:values] = valuedocs.map do |xml_item_value|
91
92
  ItemValue.from_xml(xml_item_value,path)
@@ -8,6 +8,7 @@ module AMEE
8
8
  @choices = data ? data[:choices] : []
9
9
  @choice_name = data[:choice_name]
10
10
  @selections = data ? data[:selections] : []
11
+ raise AMEE::ArgumentError.new('No choice_name specified') if @choice_name.nil?
11
12
  super
12
13
  end
13
14
 
@@ -51,30 +52,26 @@ module AMEE
51
52
  data[:selections] = selections
52
53
  # Create object
53
54
  DrillDown.new(data)
54
- rescue
55
+ rescue Exception
55
56
  raise AMEE::BadData.new("Couldn't load DrillDown resource from JSON data. Check that your URL is correct.\n#{json}")
56
57
  end
57
-
58
+
59
+ def self.xmlpathpreamble
60
+ '/Resources/DrillDownResource/'
61
+ end
62
+
58
63
  def self.from_xml(xml)
59
64
  # Parse XML
60
- doc = REXML::Document.new(xml)
65
+ @doc = load_xml_doc(xml)
61
66
  data = {}
62
- data[:choice_name] = REXML::XPath.first(doc, "/Resources/DrillDownResource/Choices/?ame").text
63
- choices = []
64
- REXML::XPath.each(doc, "/Resources/DrillDownResource/Choices//Choice") do |c|
65
- choices << (c.elements['Value'] || c.elements['value']).text
66
- end
67
- data[:choices] = choices
68
- selections = {}
69
- REXML::XPath.each(doc, "/Resources/DrillDownResource/Selections/Choice") do |c|
70
- name = (c.elements['Name'] || c.elements['name']).text
71
- value = (c.elements['Value'] || c.elements['value']).text
72
- selections[name] = value
73
- end
74
- data[:selections] = selections
67
+ data[:choice_name] = x('Choices/Name') || x('Choices/name')
68
+ data[:choices] = [x('Choices/Choices/Choice/Value') || x('Choices/Choice/value')].flatten
69
+ names = x('Selections/Choice/Name') || x('Selections/Choice/name')
70
+ values = x('Selections/Choice/Value') || x('Selections/Choice/value')
71
+ data[:selections] = names && values ? Hash[*(names.zip(values)).flatten] : {}
75
72
  # Create object
76
73
  DrillDown.new(data)
77
- rescue
74
+ rescue Exception
78
75
  raise AMEE::BadData.new("Couldn't load DrillDown resource from XML data. Check that your URL is correct.\n#{xml}")
79
76
  end
80
77
 
@@ -93,7 +90,7 @@ module AMEE
93
90
  drill.connection = connection
94
91
  # Done
95
92
  return drill
96
- rescue
93
+ rescue Exception
97
94
  raise AMEE::BadData.new("Couldn't load DrillDown resource. Check that your URL is correct (#{path}).\n#{response}")
98
95
  end
99
96
 
@@ -147,6 +147,10 @@ module AMEE
147
147
  raise AMEE::BadData.new("Couldn't delete ProfileItem. Check that your information is correct.")
148
148
  end
149
149
 
150
+ def full_path
151
+ "/definitions/itemDefinitions/#{@uid}"
152
+ end
153
+
150
154
  end
151
155
  end
152
156
  end
@@ -229,6 +229,10 @@ module AMEE
229
229
  # raise AMEE::BadData.new("Couldn't delete ItemValueDefinition. Check that your information is correct.")
230
230
  end
231
231
 
232
+ def full_path
233
+ "/definitions/itemDefinitions/#{itemdefuid}/itemValueDefinitions/#{uid}"
234
+ end
235
+
232
236
  end
233
237
 
234
238
  end
@@ -18,5 +18,9 @@ module AMEE
18
18
  attr_reader :path
19
19
  attr_reader :name
20
20
 
21
+ def expire_cache
22
+ @connection.expire_matching(full_path+'.*')
23
+ end
24
+
21
25
  end
22
26
  end
@@ -1,5 +1,6 @@
1
1
  module AMEE
2
2
  class Pager
3
+ extend ParseHelper
3
4
 
4
5
  def initialize(data)
5
6
  @start = data[:start]
@@ -29,17 +30,17 @@ module AMEE
29
30
 
30
31
  def self.from_xml(node)
31
32
  return nil if node.nil? || node.elements.empty?
32
- return Pager.new({:start => node.elements["Start"].text.to_i,
33
- :from => node.elements["From"].text.to_i,
34
- :to => node.elements["To"].text.to_i,
35
- :items => node.elements["Items"].text.to_i,
36
- :current_page => node.elements["CurrentPage"].text.to_i,
37
- :requested_page => node.elements["RequestedPage"].text.to_i,
38
- :next_page => node.elements["NextPage"].text.to_i,
39
- :previous_page => node.elements["PreviousPage"].text.to_i,
40
- :last_page => node.elements["LastPage"].text.to_i,
41
- :items_per_page => node.elements["ItemsPerPage"].text.to_i,
42
- :items_found => node.elements["ItemsFound"].text.to_i})
33
+ return Pager.new({:start => x("Start", :doc => node).to_i,
34
+ :from => x("From", :doc => node).to_i,
35
+ :to => x("To", :doc => node).to_i,
36
+ :items => x("Items", :doc => node).to_i,
37
+ :current_page => x("CurrentPage", :doc => node).to_i,
38
+ :requested_page => x("RequestedPage", :doc => node).to_i,
39
+ :next_page => x("NextPage", :doc => node).to_i,
40
+ :previous_page => x("PreviousPage", :doc => node).to_i,
41
+ :last_page => x("LastPage", :doc => node).to_i,
42
+ :items_per_page => x("ItemsPerPage", :doc => node).to_i,
43
+ :items_found => x("ItemsFound", :doc => node).to_i})
43
44
  end
44
45
 
45
46
  def self.from_json(node)
@@ -2,7 +2,11 @@ module ParseHelper
2
2
  def x(xpath,options = {})
3
3
  doc = options[:doc] || @doc
4
4
  preamble = options[:meta] == true ? metaxmlpathpreamble : xmlpathpreamble
5
- nodes=REXML::XPath.match(doc,"#{preamble}#{xpath}")
5
+ if doc.is_a?(Nokogiri::XML::Node)
6
+ nodes = doc.xpath("#{preamble}#{xpath}")
7
+ else
8
+ nodes=REXML::XPath.match(doc,"#{preamble}#{xpath}")
9
+ end
6
10
  if nodes.length==1
7
11
  if nodes.first.respond_to?(:text)
8
12
  return nodes.first.text
@@ -24,6 +28,11 @@ module ParseHelper
24
28
  def xmlpathpreamble
25
29
  ''
26
30
  end
31
+ def load_xml_doc(xml)
32
+ doc = Nokogiri.XML(xml)
33
+ doc.remove_namespaces!
34
+ doc
35
+ end
27
36
  private :x
28
37
 
29
38
  end
@@ -1,6 +1,7 @@
1
1
  module AMEE
2
2
  module Profile
3
3
  class ProfileList < Array
4
+ include ParseHelper
4
5
 
5
6
  def initialize(connection, options = {})
6
7
  # Load data from path
@@ -24,15 +25,17 @@ module AMEE
24
25
  end
25
26
  else
26
27
  # Read XML
27
- doc = REXML::Document.new(response)
28
- @pager = AMEE::Pager.from_xml(REXML::XPath.first(doc, '/Resources/ProfilesResource/Pager'))
29
- REXML::XPath.each(doc, '/Resources/ProfilesResource/Profiles/Profile') do |p|
28
+ @doc = load_xml_doc(response)
29
+ @pager = AMEE::Pager.from_xml(@doc.xpath('/Resources/ProfilesResource/Pager').first)
30
+ @doc.xpath('/Resources/ProfilesResource/Profiles/Profile').each do |p|
30
31
  data = {}
31
- data[:uid] = p.attributes['uid'].to_s
32
- data[:created] = DateTime.parse(p.attributes['created'].to_s)
33
- data[:modified] = DateTime.parse(p.attributes['modified'].to_s)
34
- data[:name] = p.elements['Name'].text || data[:uid]
35
- data[:path] = "/#{p.elements['Path'].text || data[:uid]}"
32
+ data[:uid] = x '@uid', :doc => p
33
+ data[:created] = DateTime.parse(x "@created", :doc => p)
34
+ data[:modified] = DateTime.parse(x "@modified", :doc => p)
35
+ data[:name] = x('Name', :doc => p)
36
+ data[:name] = data[:uid] if data[:name].blank?
37
+ data[:path] = x('Path', :doc => p)
38
+ data[:path] = "/#{data[:uid]}" if data[:path].blank?
36
39
  # Create profile
37
40
  profile = Profile.new(data)
38
41
  # Store connection in profile object
@@ -56,6 +59,10 @@ module AMEE
56
59
  ProfileList.new(connection)
57
60
  end
58
61
 
62
+ def self.xmlpathpreamble
63
+ '/Resources/ProfilesResource/Profile/'
64
+ end
65
+
59
66
  def self.create(connection)
60
67
  # Create new profile
61
68
  response = connection.post('/profiles', :profile => true).body
@@ -69,21 +76,22 @@ module AMEE
69
76
  data[:created] = DateTime.parse(p['created'])
70
77
  data[:modified] = DateTime.parse(p['modified'])
71
78
  data[:name] = p['name']
72
- data[:path] = p['path']
79
+ data[:path] = "/#{p['path']}"
73
80
  # Create profile
74
81
  profile = Profile.new(data)
75
82
  # Done
76
83
  return profile
77
84
  else
78
85
  # Read XML
79
- doc = REXML::Document.new(response)
80
- p = REXML::XPath.first(doc, '/Resources/ProfilesResource/Profile')
86
+ @doc = load_xml_doc(response)
81
87
  data = {}
82
- data[:uid] = p.attributes['uid'].to_s
83
- data[:created] = DateTime.parse(p.attributes['created'].to_s)
84
- data[:modified] = DateTime.parse(p.attributes['modified'].to_s)
85
- data[:name] = p.elements['Name'].text || data[:uid]
86
- data[:path] = p.elements['Path'].text || data[:uid]
88
+ data[:uid] = x '@uid'
89
+ data[:created] = DateTime.parse(x '@created')
90
+ data[:modified] = DateTime.parse(x '@modified')
91
+ data[:name] = x 'Name'
92
+ data[:name] = data[:uid] if data[:name].blank?
93
+ data[:path] = x 'Path'
94
+ data[:path] = "/#{data[:uid]}" if data[:path].blank?
87
95
  # Create profile
88
96
  profile = Profile.new(data)
89
97
  # Store connection in profile object
@@ -151,28 +151,32 @@ module AMEE
151
151
  raise AMEE::BadData.new("Couldn't load ProfileItem from XML data. Check that your URL is correct.\n#{xml}")
152
152
  end
153
153
 
154
+ def self.xmlpathpreamble
155
+ "/Resources/ProfileItemResource/"
156
+ end
157
+
154
158
  def self.from_v2_xml(xml)
155
159
  # Parse XML
156
- doc = REXML::Document.new(xml)
160
+ @doc = load_xml_doc(xml)
157
161
  data = {}
158
- data[:profile_uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/Profile/@uid").to_s
159
- data[:data_item_uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/DataItem/@uid").to_s
160
- data[:uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/@uid").to_s
161
- data[:name] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/Name').text
162
- data[:path] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/Path').text || ""
163
- data[:total_amount] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/Amount').text.to_f rescue nil
164
- data[:total_amount_unit] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/Amount/@unit').to_s rescue nil
165
- data[:start_date] = DateTime.parse(REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/StartDate").text)
166
- data[:end_date] = DateTime.parse(REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/EndDate").text) rescue nil
162
+ data[:profile_uid] = x 'Profile/@uid'
163
+ data[:data_item_uid] = x 'DataItem/@uid'
164
+ data[:uid] = x 'ProfileItem/@uid'
165
+ data[:name] = x 'ProfileItem/Name'
166
+ data[:path] = x('Path') || ""
167
+ data[:total_amount] = x('ProfileItem/Amount').to_f rescue nil
168
+ data[:total_amount_unit] = x 'ProfileItem/Amount/@unit' rescue nil
169
+ data[:start_date] = DateTime.parse(x 'ProfileItem/StartDate')
170
+ data[:end_date] = DateTime.parse(x 'ProfileItem/EndDate') rescue nil
167
171
  data[:values] = []
168
- REXML::XPath.each(doc, '/Resources/ProfileItemResource/ProfileItem/ItemValues/ItemValue') do |item|
172
+ @doc.xpath("#{xmlpathpreamble}ProfileItem/ItemValues/ItemValue").each do |item|
169
173
  value_data = {}
170
174
  item.elements.each do |element|
171
175
  key = element.name
172
176
  value = element.text
173
177
  case key
174
178
  when 'Name', 'Path', 'Value', 'Unit'
175
- value_data[key.downcase.to_sym] = value
179
+ value_data[key.downcase.to_sym] = value.blank? ? nil : value
176
180
  when 'PerUnit'
177
181
  value_data[:per_unit] = value
178
182
  end
@@ -180,7 +184,7 @@ module AMEE
180
184
  value_data[:uid] = item.attributes['uid'].to_s
181
185
  data[:values] << value_data
182
186
  end
183
- data[:amounts] = REXML::XPath.each(doc, '/Resources/ProfileItemResource/ProfileItem/Amounts/Amount').map do |item|
187
+ data[:amounts] = @doc.xpath('/Resources/ProfileItemResource/ProfileItem/Amounts/Amount').map do |item|
184
188
  x = {
185
189
  :type => item.attribute('type').value,
186
190
  :value => item.text.to_f,
@@ -190,7 +194,7 @@ module AMEE
190
194
  x[:default] = (item.attribute('default').value == 'true') if item.attribute('default')
191
195
  x
192
196
  end
193
- data[:notes] = REXML::XPath.each(doc, '/Resources/ProfileItemResource/ProfileItem/Amounts/Note').map do |item|
197
+ data[:notes] = @doc.xpath('/Resources/ProfileItemResource/ProfileItem/Amounts/Note').map do |item|
194
198
  {
195
199
  :type => item.attribute('type').value,
196
200
  :value => item.text,
@@ -12,6 +12,13 @@ module AMEE
12
12
  if $AMEE_CONFIG['ssl'] == false
13
13
  options.merge! :ssl => false
14
14
  end
15
+ if $AMEE_CONFIG['cache'] == 'rails'
16
+ # Pass in the rails cache store
17
+ options[:cache_store] = ActionController::Base.cache_store
18
+ else
19
+ options[:cache] ||= $AMEE_CONFIG['cache'] if $AMEE_CONFIG['cache'].present?
20
+ end
21
+ options[:enable_debug] ||= $AMEE_CONFIG['debug'] if $AMEE_CONFIG['debug'].present?
15
22
  @connection = self.connect($AMEE_CONFIG['server'], $AMEE_CONFIG['username'], $AMEE_CONFIG['password'], options)
16
23
  # Also store as $amee for backwards compatibility, though this is now deprecated
17
24
  $amee = @connection
@@ -15,7 +15,6 @@ module AMEE
15
15
  @email = data[:email]
16
16
  @status = data[:status]
17
17
  @api_version = data[:api_version].to_f rescue nil
18
- @environment_uid = data[:environment_uid]
19
18
  super
20
19
  end
21
20
 
@@ -36,7 +35,6 @@ module AMEE
36
35
  # Read JSON
37
36
  doc = JSON.parse(json)
38
37
  data = {}
39
- data[:environment_uid] = doc['user']['environment']['uid']
40
38
  data[:uid] = doc['user']['uid']
41
39
  data[:created] = DateTime.parse(doc['user']['created'])
42
40
  data[:modified] = DateTime.parse(doc['user']['modified'])
@@ -55,7 +53,6 @@ module AMEE
55
53
  # Parse data from response into hash
56
54
  doc = REXML::Document.new(xml)
57
55
  data = {}
58
- data[:environment_uid] = REXML::XPath.first(doc, "//Environment/@uid").to_s
59
56
  data[:uid] = REXML::XPath.first(doc, "//User/@uid").to_s
60
57
  data[:created] = DateTime.parse(REXML::XPath.first(doc, "//User/@created").to_s)
61
58
  data[:modified] = DateTime.parse(REXML::XPath.first(doc, "//User/@modified").to_s)
@@ -104,8 +101,7 @@ module AMEE
104
101
  end
105
102
 
106
103
  def full_path
107
- prefix = @environment_uid ? "/environments/#{@environment_uid}" : "/admin"
108
- "#{prefix}/users/#{uid}"
104
+ "/admin/users/#{uid}"
109
105
  end
110
106
 
111
107
  end
@@ -2,7 +2,7 @@ module AMEE
2
2
 
3
3
  module VERSION #:nodoc:
4
4
  MAJOR = 2
5
- MINOR = 4
5
+ MINOR = 5
6
6
  TINY = 0
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amee
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
- - 4
8
+ - 5
9
9
  - 0
10
- version: 2.4.0
10
+ version: 2.5.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - James Smith
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-12 00:00:00 +01:00
18
+ date: 2010-11-03 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -76,6 +76,23 @@ dependencies:
76
76
  version: "0"
77
77
  type: :runtime
78
78
  version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: nokogiri
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ hash: 113
88
+ segments:
89
+ - 1
90
+ - 4
91
+ - 3
92
+ - 1
93
+ version: 1.4.3.1
94
+ type: :runtime
95
+ version_requirements: *id005
79
96
  description:
80
97
  email: james@floppy.org.uk
81
98
  executables: