amee 2.0.25
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/COPYING +19 -0
- data/README +106 -0
- data/amee.example.yml +12 -0
- data/bin/ameesh +30 -0
- data/examples/create_profile.rb +27 -0
- data/examples/create_profile_item.rb +33 -0
- data/examples/list_profiles.rb +29 -0
- data/examples/view_data_category.rb +40 -0
- data/examples/view_data_item.rb +37 -0
- data/init.rb +1 -0
- data/lib/amee.rb +63 -0
- data/lib/amee/connection.rb +236 -0
- data/lib/amee/data_category.rb +196 -0
- data/lib/amee/data_item.rb +218 -0
- data/lib/amee/data_item_value.rb +167 -0
- data/lib/amee/data_object.rb +11 -0
- data/lib/amee/drill_down.rb +102 -0
- data/lib/amee/exceptions.rb +27 -0
- data/lib/amee/item_definition.rb +158 -0
- data/lib/amee/object.rb +21 -0
- data/lib/amee/pager.rb +59 -0
- data/lib/amee/profile.rb +108 -0
- data/lib/amee/profile_category.rb +514 -0
- data/lib/amee/profile_item.rb +374 -0
- data/lib/amee/profile_item_value.rb +106 -0
- data/lib/amee/profile_object.rb +20 -0
- data/lib/amee/rails.rb +82 -0
- data/lib/amee/shell.rb +90 -0
- data/lib/amee/user.rb +111 -0
- data/lib/amee/version.rb +10 -0
- data/rails/init.rb +12 -0
- metadata +104 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module AMEE
|
4
|
+
module Data
|
5
|
+
class DrillDown < AMEE::Data::Object
|
6
|
+
|
7
|
+
def initialize(data = {})
|
8
|
+
@choices = data ? data[:choices] : []
|
9
|
+
@choice_name = data[:choice_name]
|
10
|
+
@selections = data ? data[:selections] : []
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :choices
|
15
|
+
attr_reader :choice_name
|
16
|
+
attr_reader :selections
|
17
|
+
|
18
|
+
def path=(path)
|
19
|
+
@path = path
|
20
|
+
end
|
21
|
+
|
22
|
+
def data_item_uid
|
23
|
+
return nil if @choice_name != 'uid' || @choices.size != 1
|
24
|
+
@choices[0]
|
25
|
+
end
|
26
|
+
|
27
|
+
def choose(choice)
|
28
|
+
options = []
|
29
|
+
@selections.each_pair do |key, value|
|
30
|
+
options << "#{CGI::escape(key)}=#{CGI::escape(value)}"
|
31
|
+
end
|
32
|
+
options << "#{CGI::escape(@choice_name)}=#{CGI::escape(choice)}"
|
33
|
+
query_string = options.join "&"
|
34
|
+
DrillDown.get(connection, "#{full_path}?#{query_string}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.from_json(json)
|
38
|
+
# Parse json
|
39
|
+
doc = JSON.parse(json)
|
40
|
+
data = {}
|
41
|
+
data[:choice_name] = doc['choices']['name']
|
42
|
+
choices = []
|
43
|
+
doc['choices']['choices'].each do |c|
|
44
|
+
choices << c['value']
|
45
|
+
end
|
46
|
+
data[:choices] = choices
|
47
|
+
selections = {}
|
48
|
+
doc['selections'].each do |c|
|
49
|
+
selections[c['name']] = c['value']
|
50
|
+
end
|
51
|
+
data[:selections] = selections
|
52
|
+
# Create object
|
53
|
+
DrillDown.new(data)
|
54
|
+
rescue
|
55
|
+
raise AMEE::BadData.new("Couldn't load DrillDown resource from JSON data. Check that your URL is correct.\n#{json}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.from_xml(xml)
|
59
|
+
# Parse XML
|
60
|
+
doc = REXML::Document.new(xml)
|
61
|
+
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
|
75
|
+
# Create object
|
76
|
+
DrillDown.new(data)
|
77
|
+
rescue
|
78
|
+
raise AMEE::BadData.new("Couldn't load DrillDown resource from XML data. Check that your URL is correct.\n#{xml}")
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.get(connection, path)
|
82
|
+
# Load data from path
|
83
|
+
response = connection.get(path).body
|
84
|
+
# Parse data from response
|
85
|
+
if response.is_json?
|
86
|
+
drill = DrillDown.from_json(response)
|
87
|
+
else
|
88
|
+
drill = DrillDown.from_xml(response)
|
89
|
+
end
|
90
|
+
# Store path in drill object - response does not include it
|
91
|
+
drill.path = path.split('?')[0].gsub(/^\/data/, '')
|
92
|
+
# Store connection in object for future use
|
93
|
+
drill.connection = connection
|
94
|
+
# Done
|
95
|
+
return drill
|
96
|
+
rescue
|
97
|
+
raise AMEE::BadData.new("Couldn't load DrillDown resource. Check that your URL is correct (#{path}).\n#{response}")
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module AMEE
|
2
|
+
|
3
|
+
class ArgumentError < Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
class BadData < Exception
|
7
|
+
end
|
8
|
+
|
9
|
+
class AuthFailed < Exception
|
10
|
+
end
|
11
|
+
|
12
|
+
class PermissionDenied < Exception
|
13
|
+
end
|
14
|
+
|
15
|
+
class ConnectionFailed < Exception
|
16
|
+
end
|
17
|
+
|
18
|
+
class NotFound < Exception
|
19
|
+
end
|
20
|
+
|
21
|
+
class DuplicateResource < Exception
|
22
|
+
end
|
23
|
+
|
24
|
+
class UnknownError < Exception
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module AMEE
|
2
|
+
module Admin
|
3
|
+
|
4
|
+
class ItemDefinitionList < Array
|
5
|
+
|
6
|
+
def initialize(connection, options = {})
|
7
|
+
# Load data from path
|
8
|
+
response = connection.get('/definitions/itemDefinitions', options).body
|
9
|
+
# Parse data from response
|
10
|
+
if response.is_json?
|
11
|
+
# Read JSON
|
12
|
+
doc = JSON.parse(response)
|
13
|
+
@pager = AMEE::Pager.from_json(doc['pager'])
|
14
|
+
doc['itemDefinitions'].each do |p|
|
15
|
+
data = {}
|
16
|
+
data[:uid] = p['uid']
|
17
|
+
data[:name] = p['name']
|
18
|
+
# Create ItemDefinition
|
19
|
+
item_definition = ItemDefinition.new(data)
|
20
|
+
# Store in array
|
21
|
+
self << item_definition
|
22
|
+
end
|
23
|
+
else
|
24
|
+
# Read XML
|
25
|
+
doc = REXML::Document.new(response)
|
26
|
+
@pager = AMEE::Pager.from_xml(REXML::XPath.first(doc, '/Resources/ItemDefinitionsResource/Pager'))
|
27
|
+
REXML::XPath.each(doc, '/Resources/ItemDefinitionsResource/ItemDefinitions/ItemDefinition') do |p|
|
28
|
+
data = {}
|
29
|
+
data[:uid] = p.attributes['uid'].to_s
|
30
|
+
data[:name] = p.elements['Name'].text || data[:uid]
|
31
|
+
# Create ItemDefinition
|
32
|
+
item_definition = ItemDefinition.new(data)
|
33
|
+
# Store connection in ItemDefinition object
|
34
|
+
item_definition.connection = connection
|
35
|
+
# Store in array
|
36
|
+
self << item_definition
|
37
|
+
end
|
38
|
+
end
|
39
|
+
rescue
|
40
|
+
raise AMEE::BadData.new("Couldn't load ItemDefinition list.\n#{response}")
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :pager
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class ItemDefinition < AMEE::Object
|
48
|
+
|
49
|
+
def initialize(data = {})
|
50
|
+
@name = data[:name]
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_reader :name
|
55
|
+
|
56
|
+
def self.list(connection)
|
57
|
+
ItemDefinitionList.new(connection)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.parse(connection, response, is_list = true)
|
61
|
+
# Parse data from response
|
62
|
+
if response.is_json?
|
63
|
+
item_definition = ItemDefinition.from_json(response)
|
64
|
+
else
|
65
|
+
item_definition = ItemDefinition.from_xml(response, is_list)
|
66
|
+
end
|
67
|
+
# Store connection in object for future use
|
68
|
+
item_definition.connection = connection
|
69
|
+
# Done
|
70
|
+
return item_definition
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.from_json(json)
|
74
|
+
# Read JSON
|
75
|
+
doc = JSON.parse(json)
|
76
|
+
data = {}
|
77
|
+
data[:uid] = doc['itemDefinition']['uid']
|
78
|
+
data[:created] = DateTime.parse(doc['itemDefinition']['created'])
|
79
|
+
data[:modified] = DateTime.parse(doc['itemDefinition']['modified'])
|
80
|
+
data[:name] = doc['itemDefinition']['name']
|
81
|
+
# Create object
|
82
|
+
ItemDefinition.new(data)
|
83
|
+
rescue
|
84
|
+
raise AMEE::BadData.new("Couldn't load ItemDefinition from JSON. Check that your URL is correct.\n#{json}")
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.from_xml(xml, is_list = true)
|
88
|
+
prefix = is_list ? "/Resources/ItemDefinitionsResource/" : "/Resources/ItemDefinitionResource/"
|
89
|
+
# Parse data from response into hash
|
90
|
+
doc = REXML::Document.new(xml)
|
91
|
+
data = {}
|
92
|
+
data[:uid] = REXML::XPath.first(doc, prefix + "ItemDefinition/@uid").to_s
|
93
|
+
data[:created] = DateTime.parse(REXML::XPath.first(doc, prefix + "ItemDefinition/@created").to_s)
|
94
|
+
data[:modified] = DateTime.parse(REXML::XPath.first(doc, prefix + "ItemDefinition/@modified").to_s)
|
95
|
+
data[:name] = REXML::XPath.first(doc, prefix + "ItemDefinition/Name").text
|
96
|
+
# Create object
|
97
|
+
ItemDefinition.new(data)
|
98
|
+
rescue
|
99
|
+
raise AMEE::BadData.new("Couldn't load ItemDefinition from XML. Check that your URL is correct.\n#{xml}")
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.get(connection, path, options = {})
|
103
|
+
# Load data from path
|
104
|
+
response = connection.get(path, options).body
|
105
|
+
# Parse response
|
106
|
+
item_definition = ItemDefinition.parse(connection, response, false)
|
107
|
+
# Done
|
108
|
+
return item_definition
|
109
|
+
rescue
|
110
|
+
raise AMEE::BadData.new("Couldn't load ItemDefinition. Check that your URL is correct.\n#{response}")
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.update(connection, path, options = {})
|
114
|
+
# Do we want to automatically fetch the item afterwards?
|
115
|
+
get_item = options.delete(:get_item)
|
116
|
+
get_item = true if get_item.nil?
|
117
|
+
# Go
|
118
|
+
response = connection.put(path, options)
|
119
|
+
if get_item
|
120
|
+
if response.body.empty?
|
121
|
+
return ItemDefinition.get(connection, path)
|
122
|
+
else
|
123
|
+
return ItemDefinition.parse(connection, response.body)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
rescue
|
127
|
+
raise AMEE::BadData.new("Couldn't update ItemDefinition. Check that your information is correct.\n#{response}")
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.create(connection, options = {})
|
131
|
+
unless options.is_a?(Hash)
|
132
|
+
raise AMEE::ArgumentError.new("Second argument must be a hash of options!")
|
133
|
+
end
|
134
|
+
# Send data
|
135
|
+
response = connection.post("/definitions/itemDefinitions", options).body
|
136
|
+
# Parse response
|
137
|
+
item_definition = ItemDefinition.parse(connection, response)
|
138
|
+
# Get the ItemDefinition again
|
139
|
+
return ItemDefinition.get(connection, "/definitions/itemDefinitions/" + item_definition.uid)
|
140
|
+
rescue
|
141
|
+
raise AMEE::BadData.new("Couldn't create ItemDefinition. Check that your information is correct.\n#{response}")
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.delete(connection, item_definition)
|
145
|
+
# Deleting takes a while... up the timeout to 120 seconds temporarily
|
146
|
+
t = connection.timeout
|
147
|
+
connection.timeout = 120
|
148
|
+
connection.delete("/definitions/itemDefinitions/" + item_definition.uid)
|
149
|
+
connection.timeout = t
|
150
|
+
rescue
|
151
|
+
raise AMEE::BadData.new("Couldn't delete ProfileItem. Check that your information is correct.")
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
data/lib/amee/object.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module AMEE
|
2
|
+
class Object
|
3
|
+
|
4
|
+
def initialize(data = nil)
|
5
|
+
@uid = data ? data[:uid] : nil
|
6
|
+
@created = data ? data[:created] : Time.now
|
7
|
+
@modified = data ? data[:modified] : @created
|
8
|
+
@path = data ? data[:path] : nil
|
9
|
+
@name = data ? data[:name] : nil
|
10
|
+
@connection = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :connection
|
14
|
+
attr_reader :uid
|
15
|
+
attr_reader :created
|
16
|
+
attr_reader :modified
|
17
|
+
attr_reader :path
|
18
|
+
attr_reader :name
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/amee/pager.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module AMEE
|
2
|
+
class Pager
|
3
|
+
|
4
|
+
def initialize(data)
|
5
|
+
@start = data[:start]
|
6
|
+
@from = data[:from]
|
7
|
+
@to = data[:to]
|
8
|
+
@items = data[:items]
|
9
|
+
@current_page = data[:current_page]
|
10
|
+
@requested_page = data[:requested_page]
|
11
|
+
@next_page = data[:next_page]
|
12
|
+
@previous_page = data[:previous_page]
|
13
|
+
@last_page = data[:last_page]
|
14
|
+
@items_per_page = data[:items_per_page]
|
15
|
+
@items_found = data[:items_found]
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :start
|
19
|
+
attr_reader :from
|
20
|
+
attr_reader :to
|
21
|
+
attr_reader :items
|
22
|
+
attr_reader :current_page
|
23
|
+
attr_reader :requested_page
|
24
|
+
attr_reader :next_page
|
25
|
+
attr_reader :previous_page
|
26
|
+
attr_reader :last_page
|
27
|
+
attr_reader :items_per_page
|
28
|
+
attr_reader :items_found
|
29
|
+
|
30
|
+
def self.from_xml(node)
|
31
|
+
return Pager.new({:start => node.elements["Start"].text.to_i,
|
32
|
+
:from => node.elements["From"].text.to_i,
|
33
|
+
:to => node.elements["To"].text.to_i,
|
34
|
+
:items => node.elements["Items"].text.to_i,
|
35
|
+
:current_page => node.elements["CurrentPage"].text.to_i,
|
36
|
+
:requested_page => node.elements["RequestedPage"].text.to_i,
|
37
|
+
:next_page => node.elements["NextPage"].text.to_i,
|
38
|
+
:previous_page => node.elements["PreviousPage"].text.to_i,
|
39
|
+
:last_page => node.elements["LastPage"].text.to_i,
|
40
|
+
:items_per_page => node.elements["ItemsPerPage"].text.to_i,
|
41
|
+
:items_found => node.elements["ItemsFound"].text.to_i})
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.from_json(node)
|
45
|
+
return Pager.new({:start => node["start"],
|
46
|
+
:from => node["from"],
|
47
|
+
:to => node["to"],
|
48
|
+
:items => node["items"],
|
49
|
+
:current_page => node["currentPage"],
|
50
|
+
:requested_page => node["requestedPage"],
|
51
|
+
:next_page => node["nextPage"],
|
52
|
+
:previous_page => node["previousPage"],
|
53
|
+
:last_page => node["lastPage"],
|
54
|
+
:items_per_page => node["itemsPerPage"],
|
55
|
+
:items_found => node["itemsFound"]})
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
data/lib/amee/profile.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
module AMEE
|
2
|
+
module Profile
|
3
|
+
class ProfileList < Array
|
4
|
+
|
5
|
+
def initialize(connection, options = {})
|
6
|
+
# Load data from path
|
7
|
+
response = connection.get('/profiles', options).body
|
8
|
+
# Parse data from response
|
9
|
+
if response.is_json?
|
10
|
+
# Read JSON
|
11
|
+
doc = JSON.parse(response)
|
12
|
+
@pager = AMEE::Pager.from_json(doc['pager'])
|
13
|
+
doc['profiles'].each do |p|
|
14
|
+
data = {}
|
15
|
+
data[:uid] = p['uid']
|
16
|
+
data[:created] = DateTime.parse(p['created'])
|
17
|
+
data[:modified] = DateTime.parse(p['modified'])
|
18
|
+
data[:name] = p['name']
|
19
|
+
data[:path] = "/#{p['path']}"
|
20
|
+
# Create profile
|
21
|
+
profile = Profile.new(data)
|
22
|
+
# Store in array
|
23
|
+
self << profile
|
24
|
+
end
|
25
|
+
else
|
26
|
+
# 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|
|
30
|
+
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]}"
|
36
|
+
# Create profile
|
37
|
+
profile = Profile.new(data)
|
38
|
+
# Store connection in profile object
|
39
|
+
profile.connection = connection
|
40
|
+
# Store in array
|
41
|
+
self << profile
|
42
|
+
end
|
43
|
+
end
|
44
|
+
rescue
|
45
|
+
raise AMEE::BadData.new("Couldn't load Profile list.\n#{response}")
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :pager
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class Profile < AMEE::Profile::Object
|
53
|
+
|
54
|
+
# backwards compatibility
|
55
|
+
def self.list(connection)
|
56
|
+
ProfileList.new(connection)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.create(connection)
|
60
|
+
# Create new profile
|
61
|
+
response = connection.post('/profiles', :profile => true).body
|
62
|
+
# Parse data from response
|
63
|
+
if response.is_json?
|
64
|
+
# Read JSON
|
65
|
+
doc = JSON.parse(response)
|
66
|
+
p = doc['profile']
|
67
|
+
data = {}
|
68
|
+
data[:uid] = p['uid']
|
69
|
+
data[:created] = DateTime.parse(p['created'])
|
70
|
+
data[:modified] = DateTime.parse(p['modified'])
|
71
|
+
data[:name] = p['name']
|
72
|
+
data[:path] = p['path']
|
73
|
+
# Create profile
|
74
|
+
profile = Profile.new(data)
|
75
|
+
# Done
|
76
|
+
return profile
|
77
|
+
else
|
78
|
+
# Read XML
|
79
|
+
doc = REXML::Document.new(response)
|
80
|
+
p = REXML::XPath.first(doc, '/Resources/ProfilesResource/Profile')
|
81
|
+
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]
|
87
|
+
# Create profile
|
88
|
+
profile = Profile.new(data)
|
89
|
+
# Store connection in profile object
|
90
|
+
profile.connection = connection
|
91
|
+
# Done
|
92
|
+
return profile
|
93
|
+
end
|
94
|
+
rescue
|
95
|
+
raise AMEE::BadData.new("Couldn't create Profile.\n#{response}")
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.delete(connection, uid)
|
99
|
+
# Deleting profiles takes a while... up the timeout to 60 seconds temporarily
|
100
|
+
t = connection.timeout
|
101
|
+
connection.timeout = 60
|
102
|
+
connection.delete("/profiles/#{uid}")
|
103
|
+
connection.timeout = t
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|