amee 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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: