familysearch 0.2.0 → 0.3.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.
@@ -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