wcc-arena 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +71 -0
  7. data/Rakefile +45 -0
  8. data/lib/wcc/arena.rb +15 -0
  9. data/lib/wcc/arena/address.rb +21 -0
  10. data/lib/wcc/arena/config.rb +39 -0
  11. data/lib/wcc/arena/email.rb +10 -0
  12. data/lib/wcc/arena/group.rb +29 -0
  13. data/lib/wcc/arena/group_member.rb +35 -0
  14. data/lib/wcc/arena/group_member_query.rb +32 -0
  15. data/lib/wcc/arena/group_query.rb +36 -0
  16. data/lib/wcc/arena/mappers.rb +6 -0
  17. data/lib/wcc/arena/mappers/xml.rb +131 -0
  18. data/lib/wcc/arena/modify_result.rb +10 -0
  19. data/lib/wcc/arena/person.rb +67 -0
  20. data/lib/wcc/arena/person_attribute.rb +12 -0
  21. data/lib/wcc/arena/person_attribute_group.rb +12 -0
  22. data/lib/wcc/arena/person_attribute_query.rb +29 -0
  23. data/lib/wcc/arena/person_query.rb +69 -0
  24. data/lib/wcc/arena/phone.rb +15 -0
  25. data/lib/wcc/arena/profile.rb +35 -0
  26. data/lib/wcc/arena/profile_member.rb +17 -0
  27. data/lib/wcc/arena/profile_member_query.rb +31 -0
  28. data/lib/wcc/arena/profile_member_save.rb +100 -0
  29. data/lib/wcc/arena/profile_query.rb +56 -0
  30. data/lib/wcc/arena/response.rb +22 -0
  31. data/lib/wcc/arena/session.rb +103 -0
  32. data/lib/wcc/arena/signed_path.rb +36 -0
  33. data/lib/wcc/arena/version.rb +5 -0
  34. data/spec/fixtures/group_member_list.xml +138 -0
  35. data/spec/fixtures/modify_result_failure.xml +6 -0
  36. data/spec/fixtures/modify_result_success.xml +6 -0
  37. data/spec/fixtures/person.xml +89 -0
  38. data/spec/fixtures/person_attribute_list.xml +59 -0
  39. data/spec/fixtures/person_group_list.xml +52 -0
  40. data/spec/fixtures/person_list.xml +75 -0
  41. data/spec/fixtures/profile_list.xml +68 -0
  42. data/spec/fixtures/profile_member_list.xml +41 -0
  43. data/spec/fixtures/profile_member_save.xml +14 -0
  44. data/spec/spec_helper.rb +19 -0
  45. data/spec/support/fixtures_helpers.rb +15 -0
  46. data/spec/wcc/arena/config_spec.rb +46 -0
  47. data/spec/wcc/arena/group_member_query_spec.rb +56 -0
  48. data/spec/wcc/arena/group_member_spec.rb +40 -0
  49. data/spec/wcc/arena/group_query_spec.rb +56 -0
  50. data/spec/wcc/arena/group_spec.rb +21 -0
  51. data/spec/wcc/arena/mappers/xml_spec.rb +246 -0
  52. data/spec/wcc/arena/modify_result_spec.rb +33 -0
  53. data/spec/wcc/arena/person_attribute_group_spec.rb +10 -0
  54. data/spec/wcc/arena/person_attribute_query_spec.rb +53 -0
  55. data/spec/wcc/arena/person_attribute_spec.rb +10 -0
  56. data/spec/wcc/arena/person_query_spec.rb +87 -0
  57. data/spec/wcc/arena/person_spec.rb +58 -0
  58. data/spec/wcc/arena/profile_member_query_spec.rb +56 -0
  59. data/spec/wcc/arena/profile_member_save_spec.rb +152 -0
  60. data/spec/wcc/arena/profile_member_spec.rb +39 -0
  61. data/spec/wcc/arena/profile_query_spec.rb +77 -0
  62. data/spec/wcc/arena/profile_spec.rb +33 -0
  63. data/spec/wcc/arena/response_spec.rb +43 -0
  64. data/spec/wcc/arena/session_spec.rb +196 -0
  65. data/spec/wcc/arena/signed_path_spec.rb +81 -0
  66. data/wcc-arena.gemspec +27 -0
  67. metadata +236 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 847a7b9b46ead985eb00221fb3ce685215671746
4
+ data.tar.gz: 7ce4cea280b3de3c7761e64619d5d3af2083caab
5
+ SHA512:
6
+ metadata.gz: 23dda6c3fc79b84e9934f985c73ebc1d64dff57244b5780c24e05c8bbebb0d0a995dbf955398f5f941afc9dc7f9d1898b408d80b18cd0e3ee3cbd3861fe81d72
7
+ data.tar.gz: 5b504562dd6e74c74fec426714500952a07a6410facf94f41765df9b7753487502aebe295efa6622daf75f1cb7154b2ee46e240079bf1934c00f8988e8d35839
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .arena-creds
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wcc-arena.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Watermark Community Church
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # WCC::Arena
2
+
3
+ This gem provides wrappers to the Arena church management system's API.
4
+ This is an early version of the library, and has had limited testing in
5
+ real world environments. Use at your own risk!
6
+
7
+ There are also bound to be a few things that are specific to our
8
+ configuration and version of Arena. This isn't intentional and we
9
+ consider that a bug that we would like to fix. We would love for this to
10
+ be a fully featured way to interact with Arena's API.
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'wcc-arena'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install wcc-arena
25
+
26
+ ## Configuration
27
+
28
+ You can configure the wcc-arena gem using the `WCC::Arena.configure`
29
+ method.
30
+
31
+ Here is an example configuration block:
32
+
33
+ ```ruby
34
+ WCC::Arena.configure do |arena|
35
+ arena.username = 'username'
36
+ arena.password = 'password'
37
+ arena.api_key = 'api_key'
38
+ arena.api_secret = 'api_secret'
39
+ arena.api_url = 'https://arena-domain/api.svc/'
40
+ end
41
+ ```end
42
+
43
+ ## Usage
44
+
45
+ The library is currently a very thin layer over the Arena API. We plan
46
+ to add a higher level interface layer that provides a better experience
47
+ for the most common use cases.
48
+
49
+ The library consists of Query classes and Mapper classes. The Query
50
+ classes handle calling the respective services and the Mapper classes
51
+ handle binding the XML to Ruby objects. For full details on all
52
+ available endpoints please see the code. Below are a few examples of
53
+ some common queries.
54
+
55
+ ```ruby
56
+ query = WCC::Arena::PersonQuery.new.where(first_name: "Travis")
57
+ people = query.call
58
+ people.each do |person|
59
+ puts person.full_name
60
+ end
61
+ ```
62
+ This will print the full names of all person records with the first name
63
+ "Travis".
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create new Pull Request
@@ -0,0 +1,45 @@
1
+ require "bundler/gem_tasks"
2
+ require "json"
3
+
4
+ PROJECT_PATH = File.dirname(__FILE__)
5
+ LIB_PATH = File.join(PROJECT_PATH, "lib")
6
+ CREDENTIALS_PATH = File.join(PROJECT_PATH, ".arena-creds")
7
+
8
+ task :default => :test
9
+
10
+ desc 'set load path and require the gem'
11
+ task :environment do
12
+ $LOAD_PATH.unshift(LIB_PATH)
13
+ require 'wcc/arena'
14
+ end
15
+
16
+ desc 'run test examples'
17
+ task :test do
18
+ exec 'rspec .'
19
+ end
20
+
21
+ desc 'open an IRB session with environment loaded'
22
+ task :irb => :environment do
23
+ require 'irb'
24
+ ARGV.clear
25
+ IRB.start
26
+ end
27
+
28
+ desc 'point the library at the instance of Arena described in .arena-creds and launch irb'
29
+ task :arena => :environment do
30
+ creds = JSON.parse(File.open(CREDENTIALS_PATH).read)
31
+ WCC::Arena.configure do |arena|
32
+ arena.username = creds['username']
33
+ arena.password = creds['password']
34
+ arena.api_key = creds['api_key']
35
+ arena.api_secret = creds['api_secret']
36
+ arena.api_url = creds['api_url']
37
+ end
38
+ $session = WCC::Arena::Session.new(
39
+ username: WCC::Arena.config.username,
40
+ password: WCC::Arena.config.password,
41
+ api_key: WCC::Arena.config.api_key,
42
+ api_secret: WCC::Arena.config.api_secret,
43
+ )
44
+ Rake::Task['irb'].invoke
45
+ end
@@ -0,0 +1,15 @@
1
+ require "wcc/arena/version"
2
+
3
+ require 'faraday'
4
+ require 'nokogiri'
5
+
6
+ require "wcc/arena/config"
7
+ require "wcc/arena/mappers"
8
+ require 'wcc/arena/modify_result'
9
+ require "wcc/arena/response"
10
+ require "wcc/arena/session"
11
+ require "wcc/arena/signed_path"
12
+
13
+ require "wcc/arena/group"
14
+ require "wcc/arena/person"
15
+ require "wcc/arena/profile"
@@ -0,0 +1,21 @@
1
+ module WCC::Arena
2
+
3
+ class Address
4
+ include WCC::Arena::Mappers::XML
5
+
6
+ attribute :id, xpath: "AddressID", type: :integer
7
+ attribute :type_id, xpath: "AddressTypeID", type: :integer
8
+ attribute :type_value, xpath: "AddressTypeValue"
9
+ attribute :city, xpath: "City"
10
+ attribute :country, xpath: "Country"
11
+ attribute :latitude, xpath: "Latitude"
12
+ attribute :longitude, xpath: "Longitude"
13
+ attribute :postal_code, xpath: "PostalCode"
14
+ attribute :primary, xpath: "Primary", type: :boolean
15
+ attribute :state, xpath: "State"
16
+ attribute :street1, xpath: "StreetLine1"
17
+ attribute :street2, xpath: "StreetLine2"
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,39 @@
1
+ module WCC::Arena
2
+ class Config
3
+ attr_accessor :username, :password, :api_key, :api_secret
4
+ attr_accessor :api_url
5
+ attr_accessor :logger, :connection
6
+
7
+ def connection
8
+ @connection ||= default_connection
9
+ end
10
+
11
+ def session
12
+ @session ||= WCC::Arena::Session.new(
13
+ connection: connection,
14
+ username: username,
15
+ password: password,
16
+ api_key: api_key,
17
+ api_secret: api_secret
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ def default_connection
24
+ Faraday.new(url: api_url) do |faraday|
25
+ faraday.request :url_encoded
26
+ faraday.response :logger
27
+ faraday.adapter Faraday.default_adapter
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.config
33
+ @config ||= Config.new
34
+ end
35
+
36
+ def self.configure
37
+ yield config
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ module WCC::Arena
2
+
3
+ class Email
4
+ include WCC::Arena::Mappers::XML
5
+
6
+ attribute :address, xpath: "Address"
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,29 @@
1
+ require 'wcc/arena/group_query'
2
+
3
+ require 'wcc/arena/address'
4
+ require 'wcc/arena/group_member'
5
+
6
+ module WCC::Arena
7
+ class Group
8
+ include WCC::Arena::Mappers::XML
9
+
10
+ attribute :id, xpath: "GroupID", type: :integer
11
+ attribute :guid, xpath: "Guid"
12
+
13
+ attribute :name, xpath: "Name"
14
+ attribute :active, xpath: "Active", type: :boolean
15
+ attribute :area_name, xpath: "AreaName"
16
+ attribute :area_id, xpath: "AreaID", type: :integer
17
+ attribute :type, xpath: "GroupTypeValue"
18
+ attribute :type_id, xpath: "GroupTypeID", type: :integer
19
+ attribute :leader_id, xpath: "LeaderID", type: :integer
20
+
21
+ has_one :address, xpath: "TargetLocation", klass: Address
22
+
23
+ alias :active? :active
24
+
25
+ def members
26
+ @members ||= GroupMemberQuery.new(group_id: id).()
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ require 'wcc/arena/group_member_query'
2
+
3
+ require 'wcc/arena/person'
4
+
5
+ module WCC::Arena
6
+
7
+ class GroupMember
8
+ include WCC::Arena::Mappers::XML
9
+
10
+ attribute :group_id, xpath: "GroupID", type: :integer
11
+ attribute :person_id, xpath: "PersonID", type: :integer
12
+
13
+ attribute :active, xpath: "Active", type: :boolean
14
+ attribute :joined_at, xpath: "DateJoined", type: :time
15
+ attribute :role, xpath: "RoleValue"
16
+ attribute :role_id, xpath: "RoleID", type: :integer
17
+ attribute :person_link, xpath: "PersonLink"
18
+
19
+ has_one :person, xpath: "PersonInformation", klass: Person
20
+
21
+ def person_guid
22
+ person_link.split("/").last
23
+ end
24
+
25
+ def full_name
26
+ person.full_name ||
27
+ [
28
+ person.first_name,
29
+ person.last_name
30
+ ].join(" ")
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,32 @@
1
+ module WCC::Arena
2
+
3
+ class GroupMemberQuery
4
+ attr_reader :session
5
+ attr_reader :group_id
6
+
7
+ def initialize(args={})
8
+ @session = args.fetch(:session) { WCC::Arena.config.session }
9
+ @group_id = args.fetch(:group_id)
10
+ end
11
+
12
+ def call
13
+ members_xml.collect do |member_xml|
14
+ GroupMember.new(member_xml)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def members_xml
21
+ query_response.xml.root.xpath("Members/GroupMember")
22
+ end
23
+
24
+ def query_response
25
+ @response ||= session.get(
26
+ "group/#{group_id}/member/list"
27
+ )
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,36 @@
1
+ module WCC::Arena
2
+
3
+ class GroupQuery
4
+ attr_reader :session
5
+ attr_reader :person_id
6
+ attr_reader :category_id
7
+
8
+ def initialize(args={})
9
+ @session = args.fetch(:session) { WCC::Arena.config.session }
10
+ @person_id = args.fetch(:person_id)
11
+ @category_id = args.fetch(:category_id)
12
+ end
13
+
14
+ def call
15
+ response_groups_xml.collect do |person_xml|
16
+ Group.new(person_xml)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def response_groups_xml
23
+ query_response.xml.root.xpath("Groups/Group")
24
+ end
25
+
26
+ def query_response
27
+ @response ||= session.get(
28
+ "person/#{person_id}/group/list",
29
+ categoryid: category_id
30
+ )
31
+ end
32
+
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,6 @@
1
+ module WCC::Arena
2
+ module Mappers
3
+ end
4
+ end
5
+
6
+ require 'wcc/arena/mappers/xml'
@@ -0,0 +1,131 @@
1
+ require 'date'
2
+ require 'time'
3
+
4
+ module WCC::Arena::Mappers
5
+
6
+ module XML
7
+
8
+ attr_reader :document
9
+
10
+ def initialize(document)
11
+ @document = document
12
+ end
13
+
14
+ def [](attribute)
15
+ if self.class.attributes[attribute]
16
+ load_attribute(attribute)
17
+ else
18
+ raise KeyError, "Attribute #{attribute} is not defined"
19
+ end
20
+ end
21
+
22
+ def attributes
23
+ self.class.attributes.each_with_object({}) do |(name, options), hash|
24
+ hash[name] = self[name]
25
+ end
26
+ end
27
+
28
+ def inspect
29
+ "<#{self.class.name} " \
30
+ "#{attributes.collect { |(name, value)| [name, value.inspect].join("=") }.join(" ")}>"
31
+ end
32
+
33
+ def load_association(name)
34
+ if config = self.class.associations[name]
35
+ list = document.xpath(config[:xpath]).collect do |node|
36
+ config[:klass].new(node)
37
+ end
38
+ case config[:type]
39
+ when :many
40
+ list
41
+ when :one
42
+ list.first
43
+ end
44
+ end
45
+ end
46
+ private :load_association
47
+
48
+ def load_attribute(attribute)
49
+ config = self.class.attributes[attribute]
50
+ node = document.xpath(config[:xpath])
51
+ cast_attribute(node.text, config[:type])
52
+ end
53
+
54
+ def cast_attribute(text, type)
55
+ return nil if text.empty?
56
+
57
+ case type
58
+ when :integer
59
+ Integer(text)
60
+ when :date
61
+ Date.strptime(text.split("T").first, "%Y-%m-%d")
62
+ when :time
63
+ Time.parse(text)
64
+ when :boolean
65
+ text.downcase == "true"
66
+ else
67
+ text
68
+ end
69
+ end
70
+
71
+ module ClassMethods
72
+
73
+ def attribute(name, options)
74
+ options.fetch(:xpath) {
75
+ raise ArgumentError, ":xpath is a required argument to the `attribute' method"
76
+ }
77
+ attributes[name] = options.freeze
78
+ define_method(name) { self[name] }
79
+ end
80
+
81
+ def attributes
82
+ @attributes ||= {}
83
+ end
84
+
85
+ def associations
86
+ @associations ||= {}
87
+ end
88
+
89
+ def has_one(name, options)
90
+ add_association(name, options.merge(type: :one)) do
91
+ @association_cache ||= {}
92
+ @association_cache[name] ||= load_association(name)
93
+ end
94
+ end
95
+
96
+ def has_many(name, options)
97
+ add_association(name, options.merge(type: :many)) do
98
+ @association_cache ||= {}
99
+ @association_cache[name] ||= load_association(name)
100
+ end
101
+ end
102
+
103
+ def add_association(name, options, &block)
104
+ options.fetch(:xpath) {
105
+ raise ArgumentError, ":xpath is a required argument"
106
+ }
107
+ options.fetch(:klass) {
108
+ raise ArgumentError, ":klass is a required argument"
109
+ }
110
+ options.fetch(:type) {
111
+ raise ArgumentError, ":type is a required argument"
112
+ }
113
+ associations[name] = options.freeze
114
+ define_method(name, &block) if block_given?
115
+ end
116
+
117
+ def inherited(subclass)
118
+ super
119
+ subclass.instance_variable_set(:@attributes, attributes.dup)
120
+ subclass.instance_variable_set(:@associations, associations.dup)
121
+ end
122
+
123
+ end
124
+
125
+ def self.included(receiver)
126
+ receiver.extend ClassMethods
127
+ end
128
+
129
+ end
130
+
131
+ end