familysearch 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ workspace/*.rb
18
+
19
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
20
+ #
21
+ # * Create a file at ~/.gitignore
22
+ # * Include files you want ignored
23
+ # * Run: git config --global core.excludesfile ~/.gitignore
24
+ #
25
+ # After doing this, these files will be ignored in all your git projects,
26
+ # saving you from having to 'pollute' every project you touch with them
27
+ #
28
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
29
+ #
30
+ # For MacOS:
31
+ #
32
+ #.DS_Store
33
+ #
34
+ # For TextMate
35
+ #*.tmproj
36
+ #tmtags
37
+ #
38
+ # For emacs:
39
+ #*~
40
+ #\#*
41
+ #.\#*
42
+ #
43
+ # For vim:
44
+ #*.swp
data/Gemfile CHANGED
@@ -1,20 +1,4 @@
1
1
  source "https://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
5
- gem 'faraday', "~> 0.8.4"
6
- gem 'faraday_middleware', "~> 0.9.0"
7
- gem 'multi_json', "~> 1.5.0"
8
- gem "hashie", "~> 1.2.0"
9
- gem "rash", "~> 0.3.2"
10
2
 
11
- # Add dependencies to develop your gem here.
12
- # Include everything needed to run rake, tests, features, etc.
13
- group :development, :test do
14
- gem "rspec", "~> 2.13.0"
15
- gem "shoulda", "~> 3.3.2"
16
- gem "bundler", "~> 1.2.3"
17
- gem "jeweler", "~> 1.8.4"
18
- gem "vcr", "~> 2.4.0"
19
- gem "webmock", "~> 1.10.0"
20
- end
3
+ # Specify your gem's dependencies in familysearch.gemspec
4
+ gemspec
data/README.md CHANGED
@@ -8,7 +8,7 @@ You can install it from the commandline.
8
8
 
9
9
  Or add it to a Gemfile for use with Bundler
10
10
 
11
- gem "familysearch", "~> 0.1.0 "
11
+ gem "familysearch", "~> 0.3.0 "
12
12
 
13
13
 
14
14
  ## Basic Usage
@@ -20,21 +20,65 @@ Here's how to use it
20
20
 
21
21
  # Instantiate a Client object
22
22
  client = FamilySearch::Client.new :environment => :sandbox, :key => 'your-dev-key-here'
23
-
24
- # Load the Discovery resource
25
- client.discover!
26
-
23
+
27
24
  # For testing, you can use basic auth to get a session,
28
25
  # Don't do this in your production web app. Use OAuth 2.0
29
26
  client.basic_auth! 'your-username', 'your-password'
30
27
 
31
- me = client.get(client.discovery.links.current_user_person.href).body
32
-
33
- # The response object is a Rash object (Hashie extension) which allows you to traverse via dot notation.
34
- # It also allows an _ to be placed at the end of elements you are traversing to guard against nil values.
35
- me.persons[0].display_.name
28
+ # the client object has a get, post, put, delete, and head method
29
+ response = client.get(client.discovery['links']['current-user-person']['href'])
30
+ response.status #=> 200
31
+
32
+ # The response contains a Hash object on the body attribute.
33
+ response.body['persons'][0]['display']['name']
34
+
35
+ ## Discovery Resource
36
+
37
+ The FamilySearch Platform is RESTful and conforms to HATEOAS principles. This means that you can discover addressable resources in the hypermedia that is returned by the platform. As a starting point, a Discovery Resource is offered up that lists resources that you can programatically begin to explore.
38
+
39
+ The Discovery Resource is intended to be utilized heavily by this gem. One of the benefits of this is that if FamilySearch needs to change a URI for any resource, your application should continue to work because the URI will be updated in the discovery resource. URIs are not constructed by hard-coding paths within the gem. They are constructed by utilizing templates exposed via the Discovery Resource.
40
+
41
+ ### URL Templates
42
+
43
+ The `familysearch` gem makes use of the templates exposed on the Discovery Resource. For example, the discovery resource exposes a person-template that looks like this:
44
+
45
+ "person-template" : {
46
+ "template" : "https://sandbox.familysearch.org/platform/tree/persons/{pid}{?access_token}",
47
+ "type" : "application/json,application/x-fs-v1+json,application/x-fs-v1+xml,application/x-gedcomx-v1+json,application/x-gedcomx-v1+xml,application/xml,text/html",
48
+ "accept" : "application/x-fs-v1+json,application/x-fs-v1+xml,application/x-gedcomx-v1+json,application/x-gedcomx-v1+xml",
49
+ "allow" : "HEAD,GET,POST,DELETE,GET,POST",
50
+ "title" : "Person"
51
+ }
52
+
53
+ To utilize this template, you can do the following from your `client` object.
54
+
55
+ response = client.template('person-template').get :pid => 'KWQS-BBQ'
56
+
57
+ You can also use `'person'`, or `:person` instead of 'person-template'. The client will still find the appropriate template.
58
+
59
+ The above code is equivalent to the following:
60
+
61
+ response = client.get 'https://sandbox.familysearch.org/platform/tree/persons/KWQX-52J'
62
+
63
+ ## Faraday
64
+
65
+ This gem depends upon the wonderful `faraday` gem for handling HTTP calls. One of the advantages of this is that you could possibly swap out underlying http libraries. Currently, it utilizes the Net::HTTP library, but in the future it could potentially support other libraries such as EventMachine's asynchronous http library.
66
+
67
+ ## MultiJson
68
+
69
+ This gem makes use of the awesome `multi_json` gem. This allows you to utilize faster json libraries if desired, such as `oj` or `yajil`. If you have `require`ed either of these libraries prior to requiring the `familysearch` gem, then it will make use of the faster JSON parsing libraries.
70
+
71
+ ## Future
72
+
73
+ Next on the roadmap:
74
+
75
+ * Support post, put, delete, head, etc. from the FamilySearch::URLTemplate objec. (currently only supports get)
76
+ * Better object deserialization. Currently, everything is just a dumb Hash. I'd like to be able to allow an option to pass in another middleware for smarter objects (pershaps Hashie::Mash or Rash, or some sort of GedcomX model).
77
+ * More documentation examples.
78
+
79
+ ## Bugs and Feature Requests
36
80
 
37
- More documentation coming soon...
81
+ Please file bug reports and
38
82
 
39
83
  ## Copyright
40
84
 
data/Rakefile CHANGED
@@ -1,37 +1,17 @@
1
- require 'rubygems'
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "familysearch/version"
2
3
  require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'rake'
11
-
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "familysearch"
16
- gem.homepage = "http://github.com/jimmyz/familysearch-rb"
17
- gem.license = "MIT"
18
- gem.summary = %Q{A gem for the FamilySearch Platform.}
19
- gem.description = %Q{A gem for the FamilySearch Platform. Documentation for the FamilySearch Platform can be found at https://}
20
- gem.email = "jimmy.zimmerman@gmail.com"
21
- gem.authors = ["Jimmy Zimmerman"]
22
- # Include your dependencies below. Runtime dependencies are required when using your gem,
23
- # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
- # gem.add_development_dependency 'rspec', '> 1.2.3'
25
- end
26
- Jeweler::RubygemsDotOrgTasks.new
4
+ Bundler::GemHelper.install_tasks
27
5
 
28
6
  require 'rspec/core/rake_task'
29
- RSpec::Core::RakeTask.new(:spec)
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = "-c"
9
+ end
30
10
  task :default => :spec
31
11
 
32
12
  require 'rdoc/task'
33
13
  Rake::RDocTask.new do |rdoc|
34
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
14
+ version = FamilySearch::VERSION
35
15
 
36
16
  rdoc.rdoc_dir = 'rdoc'
37
17
  rdoc.title = "familysearch #{version}"
@@ -1,87 +1,28 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "familysearch/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = "familysearch"
8
- s.version = "0.2.0"
6
+ s.name = "familysearch"
7
+ s.version = FamilySearch::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jimmy Zimmerman"]
10
+ s.email = ["jimmy.zimmerman@gmail.com"]
11
+ s.homepage = "https://github.com/jimmyz/familysearch-rb"
12
+ s.summary = %q{A gem for the FamilySearch Platform.}
13
+ s.description = %q{A gem for the FamilySearch Platform. Documentation for the FamilySearch Platform can be found at https://familysearch.org/developers/.}
9
14
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Jimmy Zimmerman"]
12
- s.date = "2013-03-19"
13
- s.description = "A gem for the FamilySearch Platform. Documentation for the FamilySearch Platform can be found at https://"
14
- s.email = "jimmy.zimmerman@gmail.com"
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.md"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".rvmrc",
22
- "Gemfile",
23
- "LICENSE.txt",
24
- "README.md",
25
- "Rakefile",
26
- "VERSION",
27
- "familysearch.gemspec",
28
- "lib/familysearch.rb",
29
- "lib/familysearch/client.rb",
30
- "spec/client_spec.rb",
31
- "spec/familysearch_spec.rb",
32
- "spec/fixtures/vcr_cassettes/discovery.yml",
33
- "spec/fixtures/vcr_cassettes/discovery_auth.yml",
34
- "spec/spec_helper.rb",
35
- "workspace/notes/APIDesign.txt",
36
- "workspace/notes/discovery_links.txt",
37
- "workspace/notes/goals.txt"
38
- ]
39
- s.homepage = "http://github.com/jimmyz/familysearch-rb"
40
- s.licenses = ["MIT"]
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
41
18
  s.require_paths = ["lib"]
42
- s.rubygems_version = "1.8.24"
43
- s.summary = "A gem for the FamilySearch Platform."
44
-
45
- if s.respond_to? :specification_version then
46
- s.specification_version = 3
47
-
48
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
- s.add_runtime_dependency(%q<faraday>, ["~> 0.8.4"])
50
- s.add_runtime_dependency(%q<faraday_middleware>, ["~> 0.9.0"])
51
- s.add_runtime_dependency(%q<multi_json>, ["~> 1.5.0"])
52
- s.add_runtime_dependency(%q<hashie>, ["~> 1.2.0"])
53
- s.add_runtime_dependency(%q<rash>, ["~> 0.3.2"])
54
- s.add_development_dependency(%q<rspec>, ["~> 2.13.0"])
55
- s.add_development_dependency(%q<shoulda>, ["~> 3.3.2"])
56
- s.add_development_dependency(%q<bundler>, ["~> 1.2.3"])
57
- s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
58
- s.add_development_dependency(%q<vcr>, ["~> 2.4.0"])
59
- s.add_development_dependency(%q<webmock>, ["~> 1.10.0"])
60
- else
61
- s.add_dependency(%q<faraday>, ["~> 0.8.4"])
62
- s.add_dependency(%q<faraday_middleware>, ["~> 0.9.0"])
63
- s.add_dependency(%q<multi_json>, ["~> 1.5.0"])
64
- s.add_dependency(%q<hashie>, ["~> 1.2.0"])
65
- s.add_dependency(%q<rash>, ["~> 0.3.2"])
66
- s.add_dependency(%q<rspec>, ["~> 2.13.0"])
67
- s.add_dependency(%q<shoulda>, ["~> 3.3.2"])
68
- s.add_dependency(%q<bundler>, ["~> 1.2.3"])
69
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
70
- s.add_dependency(%q<vcr>, ["~> 2.4.0"])
71
- s.add_dependency(%q<webmock>, ["~> 1.10.0"])
72
- end
73
- else
74
- s.add_dependency(%q<faraday>, ["~> 0.8.4"])
75
- s.add_dependency(%q<faraday_middleware>, ["~> 0.9.0"])
76
- s.add_dependency(%q<multi_json>, ["~> 1.5.0"])
77
- s.add_dependency(%q<hashie>, ["~> 1.2.0"])
78
- s.add_dependency(%q<rash>, ["~> 0.3.2"])
79
- s.add_dependency(%q<rspec>, ["~> 2.13.0"])
80
- s.add_dependency(%q<shoulda>, ["~> 3.3.2"])
81
- s.add_dependency(%q<bundler>, ["~> 1.2.3"])
82
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
83
- s.add_dependency(%q<vcr>, ["~> 2.4.0"])
84
- s.add_dependency(%q<webmock>, ["~> 1.10.0"])
85
- end
86
- end
87
-
19
+
20
+ s.add_dependency("faraday", ["~> 0.8.4"])
21
+ s.add_dependency("faraday_middleware", ["~> 0.9.0"])
22
+ s.add_dependency("multi_json", ["~> 1.5.0"])
23
+ s.add_development_dependency("rspec", ["~> 2.13.0"])
24
+ s.add_development_dependency("shoulda", ["~> 3.3.2"])
25
+ s.add_development_dependency("bundler", [">= 1.2.3"])
26
+ s.add_development_dependency("vcr", ["~> 2.4.0"])
27
+ s.add_development_dependency("webmock", ["~> 1.10.0"])
28
+ end
@@ -1 +1,2 @@
1
+ require 'familysearch/version'
1
2
  require 'familysearch/client'
@@ -1,10 +1,38 @@
1
- require 'hashie'
2
1
  require 'faraday'
3
2
  require 'faraday_middleware'
4
3
  require 'forwardable'
4
+ require 'familysearch/url_template'
5
+ require 'familysearch/error'
6
+ require 'familysearch/middleware'
5
7
 
8
+ # Namespace this gem functionality under FamilySearch. I realise it doesn't exactly follow gem conventions by CamelCasing FamilySearch,
9
+ # but I'm just following FamilySearch branding (familysearch.org, not family_search.org and FamilySearch not Familysearch).
6
10
  module FamilySearch
11
+ # FamilySearch::Client is the core of the +familysearch+ gem. It manages the HTTP requests to
12
+ # the FamilySearch Platform. Under the covers, it is implemented using the wonderful Faraday ruby gem.
13
+ #
14
+ # The +familysearch+ gem relies heavily on Faraday::Middleware features to handle such things such as
15
+ # following redirects, parsing JSON, logging HTTP traffic, and raising errors for non 20x or 30x responses.
16
+ #
17
+ # =Usage:
18
+ # TODO: place some examples here.
19
+ #
7
20
  class Client
21
+ # Contains a configuration for finding the discovery resource for the various systems.
22
+ # {
23
+ # :production => {
24
+ # :base_url => 'https://familysearch.org',
25
+ # :discovery_path => '/.well-known/app-meta'
26
+ # },
27
+ # :staging => {
28
+ # :base_url => 'https://stage.familysearch.org',
29
+ # :discovery_path => '/.well-known/app-meta'
30
+ # },
31
+ # :sandbox => {
32
+ # :base_url => 'https://sandbox.familysearch.org',
33
+ # :discovery_path => '/.well-known/app-meta'
34
+ # }
35
+ # }
8
36
  ENV_CONF = {
9
37
  :production => {
10
38
  :base_url => 'https://familysearch.org',
@@ -26,6 +54,22 @@ module FamilySearch
26
54
  extend Forwardable
27
55
  def_delegators :@agent, :get, :put, :post, :delete, :head, :options
28
56
 
57
+ # Initializing a FamilySearch::Client object.
58
+ #
59
+ # *Args* :
60
+ # - +options+: An Hash containing any of the following configuration items.
61
+ # - +:key+: Your developer key.
62
+ # - +:environment+: Accepts the following values: :production, :staging, :sandbox.
63
+ # It defaults to :sandbox.
64
+ # - +:base_url+: If you would like to override the base url of the production, staging,
65
+ # or sandbox environments, you can set this to something else like "http://localhost:8080"
66
+ # - +:access_token+: (optional) Your access token if you already have one.
67
+ # - +:logger+: (optional) An object that conforms to the Logger interface.
68
+ # This could be a Rails logger, or another logger object that writes to
69
+ # STDOUT or to a file.
70
+ # *Returns* :
71
+ # - +FamilySearch::Client+: a client object for making requests
72
+ #
29
73
  def initialize(options = {})
30
74
  @access_token = options[:access_token] # if options[:access_token]
31
75
  @logger = options[:logger]
@@ -33,27 +77,82 @@ module FamilySearch
33
77
  @environment = options[:environment] || :sandbox
34
78
  @base_url = options[:base_url] || ENV_CONF[@environment][:base_url]
35
79
  @agent = Faraday.new(@base_url) do |faraday|
80
+ faraday.response :familysearch_errors
36
81
  faraday.response :logger, options[:logger] if options[:logger]
37
- faraday.response :rashify
38
- faraday.response :json
82
+ faraday.response :multi_json
39
83
  faraday.response :follow_redirects, :limit => 3, :standards_compliant => true
40
84
  faraday.headers['Accept'] = 'application/x-fs-v1+json'
41
85
  faraday.authorization('Bearer',@access_token) if @access_token
42
86
  faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
43
87
  end
44
88
  end
45
-
89
+
90
+ # This method gets information from the {Discovery Resource}[https://familysearch.org/.well-known/app-meta.json].
91
+ #
92
+ # This call will cache the discovery resource in an attribute called @discovery.
93
+ #
94
+ # Calling this multiple times should be very cheap because of the caching. a FamilySearch::Client object will only
95
+ # ever make an HTTP request for the discovery resource *once* in its lifetime.
96
+ #
97
+ # * *Returns* :
98
+ # - +discovery+: the value of the @discovery attribute now that it has been populated.
99
+ #
46
100
  def discover!
47
101
  @discovery ||= get_discovery
48
102
  end
49
103
 
104
+ # Performs an authentication against the /identity/v2/login resource. It uses the
105
+ # {Discovery Resource}[https://familysearch.org/.well-known/app-meta.json] to determine the URL to make the request to
106
+ # in case the URL ever changes. This is only to be used for testing/development.
107
+ #
108
+ # *Note*: You may *NOT* use this method for building web applications. All web applications must use OAuth/OAuth2.
109
+ # Your web application will not be certified if it prompts for user credentials within the application. Also, you may not use
110
+ # your personal credentials to authenticate your system in behalf of a user.
111
+ #
112
+ # *Args* :
113
+ # - +username+: A FamilySearch username.
114
+ # - +password+: The user's password.
115
+ # - +key+ (optional): Your developer key if it wasn't already set when you initialized the FamilySearch::Client
116
+ # *Returns* :
117
+ # - true
118
+ # *Raises* :
119
+ # - +FamilySearch::Error::BadCredentials+: If it cannot authenticate
120
+ #
50
121
  def basic_auth!(username,password,key=nil)
51
122
  self.discover!
52
123
  @key ||= key if key
53
124
  @agent.basic_auth username, password
54
- response = @agent.get @discovery.links.fs_identity_v2_login.href, :dataFormat => 'application/json', :key => @key
55
- @access_token = response.body.session.id
125
+ response = @agent.get @discovery['links']['fs-identity-v2-login']['href'], :dataFormat => 'application/json', :key => @key
126
+ @access_token = response.body['session']['id']
56
127
  @agent.authorization('Bearer',@access_token)
128
+ return true
129
+ end
130
+
131
+ # Used for taking advantage of URL templates provided by the {Discovery Resource}[https://familysearch.org/.well-known/app-meta.json].
132
+ #
133
+ # This method will automatically call the FamilySearch::Client#discover! method in order to populate the discovery resources.
134
+ #
135
+ # ===Usage:
136
+ #
137
+ # client = FamilySearch::Client.new
138
+ # res = client.template('person').get :pid => 'KWQS-BBQ'
139
+ # res.body['persons'][0]['id] # => 'KWQS-BBQ'
140
+ #
141
+ # Please note, only the +get+ method has been implemented on the URLTemplate object. POST, PUT, and DELETE should be pretty easy
142
+ # to add. It just hasn't been a priority yet.
143
+ #
144
+ # *Args* :
145
+ # - +template_name+: The name of the template. For the "person-template", you can pass "person-template", "person", or :person
146
+ # *Returns* :
147
+ # - FamilySearch::URLTemplate object
148
+ # *Raises* :
149
+ # - +FamilySearch::Error::URLTemplateNotFound+: if the template is not found.
150
+ #
151
+ def template(template_name)
152
+ self.discover!
153
+ k = template_name.to_s
154
+ template = @discovery['links'][k] || @discovery['links'][k+'-template'] || @discovery['links'][k+'-query']
155
+ FamilySearch::URLTemplate.new self, template
57
156
  end
58
157
 
59
158
  private