pipejump 0.2.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.
- data/.document +5 -0
- data/.gitignore +25 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +25 -0
- data/LICENSE +20 -0
- data/README.rdoc +120 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/lib/pipejump/base/collection.rb +117 -0
- data/lib/pipejump/base/connection.rb +54 -0
- data/lib/pipejump/base/errors.rb +11 -0
- data/lib/pipejump/base/resource.rb +182 -0
- data/lib/pipejump/base/session.rb +172 -0
- data/lib/pipejump/resources/account.rb +20 -0
- data/lib/pipejump/resources/client.rb +162 -0
- data/lib/pipejump/resources/contact.rb +225 -0
- data/lib/pipejump/resources/deal.rb +246 -0
- data/lib/pipejump/resources/note.rb +159 -0
- data/lib/pipejump/resources/reminder.rb +171 -0
- data/lib/pipejump/resources/source.rb +159 -0
- data/lib/pipejump.rb +19 -0
- data/spec/connection.sample.yml +3 -0
- data/spec/pipejump/resources/client_spec.rb +119 -0
- data/spec/pipejump/resources/contact_spec.rb +93 -0
- data/spec/pipejump/resources/deal_spec.rb +115 -0
- data/spec/pipejump/resources/note_spec.rb +100 -0
- data/spec/pipejump/resources/reminder_spec.rb +120 -0
- data/spec/pipejump/resources/source_spec.rb +91 -0
- data/spec/pipejump/session_spec.rb +44 -0
- data/spec/spec_helper.rb +12 -0
- metadata +117 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
rdoc
|
19
|
+
pkg
|
20
|
+
|
21
|
+
## PROJECT::SPECIFIC
|
22
|
+
spec/connection.yml
|
23
|
+
coverage
|
24
|
+
coverage.data
|
25
|
+
test*.rb
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
gemcutter (0.5.0)
|
5
|
+
json_pure
|
6
|
+
git (1.2.5)
|
7
|
+
jeweler (1.4.0)
|
8
|
+
gemcutter (>= 0.1.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rubyforge (>= 2.0.0)
|
11
|
+
json_pure (1.4.6)
|
12
|
+
rake (0.8.7)
|
13
|
+
rcov (0.9.8)
|
14
|
+
rspec (1.3.0)
|
15
|
+
rubyforge (2.0.3)
|
16
|
+
json_pure (>= 1.1.7)
|
17
|
+
|
18
|
+
PLATFORMS
|
19
|
+
ruby
|
20
|
+
|
21
|
+
DEPENDENCIES
|
22
|
+
jeweler
|
23
|
+
rake
|
24
|
+
rcov
|
25
|
+
rspec (= 1.3.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Marcin Bunsch
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
*pipejump* gem is a Ruby-based client for the API of the Pipejump sales app.
|
2
|
+
|
3
|
+
== Requirements
|
4
|
+
|
5
|
+
There are no dependencies required to use this code.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
The gem available via Rubygems. To install it, use the following command:
|
10
|
+
|
11
|
+
|
12
|
+
sudo gem install pipejump
|
13
|
+
|
14
|
+
|
15
|
+
To get the latest version, clone the gem from github and run rake install:
|
16
|
+
|
17
|
+
|
18
|
+
git clone http://github.com/pipejump/pipejump
|
19
|
+
cd pipejump
|
20
|
+
rake install
|
21
|
+
|
22
|
+
|
23
|
+
== Usage
|
24
|
+
|
25
|
+
Before using this gem you must require it by calling:
|
26
|
+
|
27
|
+
|
28
|
+
require 'pipejump'
|
29
|
+
|
30
|
+
|
31
|
+
=== Session
|
32
|
+
|
33
|
+
To use the client, you must first authenticate the client with the API by initializing an instance of the Pipejump::Session class. You will later use this instance for communication with the API.
|
34
|
+
|
35
|
+
|
36
|
+
@session = Pipejump::Session.new(:email => 'you@email.com', :password => 'your_password')
|
37
|
+
|
38
|
+
|
39
|
+
If the credentials are incorrect, this will raise a Pipejump::AuthenticationFailed error.
|
40
|
+
|
41
|
+
If authentication succeeds, this will return a new Pipejump::Session instance which you can use to communicate with the API.
|
42
|
+
|
43
|
+
As of version 0.1.1 you can use the token which is fetched when authenticating using the email and password. So once you get the token, you can use it for future initialization of the Session and not send the username and password, which is a more secure.
|
44
|
+
|
45
|
+
@session = Pipejump::Session.new(:token => 'your_token')
|
46
|
+
|
47
|
+
Also, as of version 0.1.1 connection is performed over SSL.
|
48
|
+
|
49
|
+
For more information on the Session, consult the wiki page at http://github.com/pipejump/pipejump/wiki/Session
|
50
|
+
|
51
|
+
To access any resources, you need a valid Session instance, referred to as @session in the following examples.
|
52
|
+
|
53
|
+
=== Account
|
54
|
+
|
55
|
+
You can access your account by calling
|
56
|
+
|
57
|
+
|
58
|
+
@session.account
|
59
|
+
|
60
|
+
|
61
|
+
For more information, consult the wiki page at http://github.com/pipejump/pipejump/wiki/Account
|
62
|
+
|
63
|
+
=== Deals
|
64
|
+
|
65
|
+
You can access your deals by calling
|
66
|
+
|
67
|
+
|
68
|
+
@session.deals
|
69
|
+
|
70
|
+
|
71
|
+
With Deals you get access to Notes, Reminders and Deal Contacts.
|
72
|
+
|
73
|
+
For more information, consult the wiki page at http://github.com/pipejump/pipejump/wiki/Deals
|
74
|
+
|
75
|
+
=== Clients
|
76
|
+
|
77
|
+
You can access your clients by calling
|
78
|
+
|
79
|
+
|
80
|
+
@session.clients
|
81
|
+
|
82
|
+
|
83
|
+
For more information, consult the wiki page at http://github.com/pipejump/pipejump/wiki/Clients
|
84
|
+
|
85
|
+
=== Contacts
|
86
|
+
|
87
|
+
You can access your contacts by calling
|
88
|
+
|
89
|
+
|
90
|
+
@session.contacts
|
91
|
+
|
92
|
+
|
93
|
+
For more information, consult the wiki page at http://github.com/pipejump/pipejump/wiki/Contacts
|
94
|
+
|
95
|
+
=== Sources
|
96
|
+
|
97
|
+
You can access your sources by calling
|
98
|
+
|
99
|
+
|
100
|
+
@session.sources
|
101
|
+
|
102
|
+
|
103
|
+
For more information, consult the wiki page at http://github.com/pipejump/pipejump/wiki/Sources
|
104
|
+
|
105
|
+
== Contribution
|
106
|
+
|
107
|
+
You're more than welcome to fork and improve this gem. Usual rules:
|
108
|
+
|
109
|
+
* Fork the project.
|
110
|
+
* Make your feature addition or bug fix.
|
111
|
+
* Add tests for it. This is important so I don't break it in a
|
112
|
+
future version unintentionally.
|
113
|
+
* Commit, do not mess with rakefile, version, or history.
|
114
|
+
(if you want to have your own version, that is fine but
|
115
|
+
bump version in a commit by itself I can ignore when I pull)
|
116
|
+
* Send me a pull request.
|
117
|
+
|
118
|
+
== Copyright
|
119
|
+
|
120
|
+
Copyright (c) 2010 Pipejump. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
require 'rake'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = "pipejump"
|
9
|
+
gem.summary = %Q{Pipejump API Ruby client}
|
10
|
+
gem.description = %Q{Pipejump API Ruby client}
|
11
|
+
gem.email = "marcin@pipejump.com"
|
12
|
+
gem.homepage = "http://github.com/pipejump/pipejump"
|
13
|
+
gem.authors = ["Marcin Bunsch"]
|
14
|
+
gem.add_development_dependency "bundler", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'rcov/rcovtask'
|
26
|
+
|
27
|
+
desc "Run rcov for rspec"
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
29
|
+
t.spec_files = FileList['spec/pipejump/**/*_spec.rb']
|
30
|
+
t.spec_opts = %w{--color}
|
31
|
+
t.rcov = true
|
32
|
+
t.rcov_opts = %w{--html --exclude osx\/objc,gems\/,spec\/}
|
33
|
+
t.rcov_opts << %[-o "coverage"]
|
34
|
+
end
|
35
|
+
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
43
|
+
t.spec_files = FileList['spec/pipejump/*_spec.rb', 'spec/pipejump/**/*_spec.rb']
|
44
|
+
t.spec_opts = %w{--color}
|
45
|
+
end
|
46
|
+
|
47
|
+
task :default => :spec
|
48
|
+
|
49
|
+
require 'rake/rdoctask'
|
50
|
+
Rake::RDocTask.new do |rdoc|
|
51
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
52
|
+
|
53
|
+
rdoc.rdoc_dir = 'rdoc'
|
54
|
+
rdoc.title = "pipejump-client #{version}"
|
55
|
+
rdoc.rdoc_files.include('README*')
|
56
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
57
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Pipejump
|
2
|
+
|
3
|
+
# Represents a collection Resources available in the Pipejump API
|
4
|
+
#
|
5
|
+
# ==== Available Collections:
|
6
|
+
# * Clients
|
7
|
+
# @session.clients
|
8
|
+
# * Contacts
|
9
|
+
# @session.contacts
|
10
|
+
# * Sources
|
11
|
+
# @session.sources
|
12
|
+
# * Notes in a Deal
|
13
|
+
# @deal.notes
|
14
|
+
# * Reminders in a Deal
|
15
|
+
# @deal.reminders
|
16
|
+
# * Contacts in a Deal
|
17
|
+
# @deal.contacts
|
18
|
+
# *Please* *note*: This Collection has the following methods disabled: find, create
|
19
|
+
#
|
20
|
+
# ==== Additional methods:
|
21
|
+
# Additionally, each collection allows shorthand calls to the Array returned via all for the following methods:
|
22
|
+
# * first
|
23
|
+
# * last
|
24
|
+
# * each
|
25
|
+
# * size
|
26
|
+
# * collect
|
27
|
+
# * reject
|
28
|
+
# ===== Example:
|
29
|
+
# @session.sources.size
|
30
|
+
# @session.sources.each { |source| }
|
31
|
+
class Collection
|
32
|
+
|
33
|
+
class << self
|
34
|
+
# Disable methods specified as arguments
|
35
|
+
def disable(*methods) #:nodoc:
|
36
|
+
methods.each do |method|
|
37
|
+
class_eval <<-STR
|
38
|
+
def #{method}; raise NotImplemented; end
|
39
|
+
STR
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create a new Collection of Resource objects
|
45
|
+
# ==== Arguments
|
46
|
+
# * _session_ - Session object
|
47
|
+
# * _resource_ - class of the Resource for this collection
|
48
|
+
# * _owner_ - a Resource object which owns the collection, applies scope to calls
|
49
|
+
# ==== Usage
|
50
|
+
# Normally you do not call the constructor directly, instead you go via the Session or Deal instance, like this:
|
51
|
+
# @session.clients
|
52
|
+
# or
|
53
|
+
# @deal.notes
|
54
|
+
#
|
55
|
+
def initialize(session, resource, owner = nil)
|
56
|
+
@session = session
|
57
|
+
@resource = resource
|
58
|
+
@owner = owner
|
59
|
+
@prefix = owner ? owner.element_path : ''
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns a path to the collection of Resource objects
|
63
|
+
def collection_path
|
64
|
+
@prefix + '/' + @resource.collection_path.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a path to a single Resource
|
68
|
+
def element_path(id)
|
69
|
+
@prefix + '/' + @resource.collection_path.to_s + '/' + id.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns a single Resource object, based on its _id_
|
73
|
+
# ==== Arguments
|
74
|
+
# * _id_ - id of Resource
|
75
|
+
def find(id)
|
76
|
+
code, data = @session.get(element_path(id) + '.json')
|
77
|
+
if code == 200
|
78
|
+
key = @resource.name.to_s.split('::').last.downcase
|
79
|
+
@resource.new(data[key].merge(:session =>@session))
|
80
|
+
elsif code == 404
|
81
|
+
raise ResourceNotFound
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns an Array of Resource objects
|
86
|
+
def all
|
87
|
+
code, data = @session.get(collection_path + '.json')
|
88
|
+
data.collect { |data|
|
89
|
+
key = @resource.name.to_s.split('::').last.downcase
|
90
|
+
@resource.new(data[key].merge(:session => @session, :prefix => @prefix))
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
# Creates and returns a Resource object
|
95
|
+
# ==== Arguments
|
96
|
+
# * _attrs_ - a Hash of attributes passed to the constructor of the Resource
|
97
|
+
def create(attrs)
|
98
|
+
resource = @resource.new(attrs.merge(:session => @session, :prefix => @prefix))
|
99
|
+
resource.save
|
100
|
+
resource
|
101
|
+
end
|
102
|
+
|
103
|
+
def inspect
|
104
|
+
all.inspect
|
105
|
+
end
|
106
|
+
|
107
|
+
['first', 'last', 'each', 'size', 'collect', 'reject'].each do |method|
|
108
|
+
class_eval <<-STR
|
109
|
+
def #{method}(*args, &block)
|
110
|
+
all.#{method}(*args, &block)
|
111
|
+
end
|
112
|
+
STR
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'uri'
|
2
|
+
module Pipejump
|
3
|
+
|
4
|
+
class Connection #:nodoc:
|
5
|
+
|
6
|
+
attr_accessor :session, :endpoint
|
7
|
+
|
8
|
+
def initialize(session, endpoint = nil)
|
9
|
+
@session = session
|
10
|
+
@endpoint = endpoint || 'https://api.pipejump.com'
|
11
|
+
end
|
12
|
+
|
13
|
+
def site
|
14
|
+
parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
15
|
+
parser.parse(self.endpoint)
|
16
|
+
end
|
17
|
+
|
18
|
+
def post(path, data)
|
19
|
+
http.post(path, data, headers)
|
20
|
+
end
|
21
|
+
|
22
|
+
def put(path, data)
|
23
|
+
http.put(path, data, headers)
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(path)
|
27
|
+
http.get(path, headers)
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(path)
|
31
|
+
http.delete(path, headers)
|
32
|
+
end
|
33
|
+
|
34
|
+
def headers
|
35
|
+
{ 'X-Pipejump-Auth' => session.token.to_s }
|
36
|
+
end
|
37
|
+
|
38
|
+
def http
|
39
|
+
instance ||= Net::HTTP.new(site.host, site.port)
|
40
|
+
if @endpoint.match(/^https:/)
|
41
|
+
instance.use_ssl = true
|
42
|
+
instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
43
|
+
end
|
44
|
+
instance
|
45
|
+
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
"#<#{self.class} endpoint: \"#{endpoint}\">"
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Pipejump
|
2
|
+
# Error raised when authentication fails
|
3
|
+
class AuthenticationFailed < Exception; end
|
4
|
+
|
5
|
+
# Error raised when a resource is not found via find
|
6
|
+
class ResourceNotFound < Exception; end
|
7
|
+
# Error raised when a method is called but is not available for that object
|
8
|
+
#
|
9
|
+
# Example: Pipejump::Collection of Contact objects within a Deal has the following methods disabled: find, create
|
10
|
+
class NotImplemented < Exception; end
|
11
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Pipejump
|
2
|
+
#
|
3
|
+
# Represents a Resource available in the Pipejump API
|
4
|
+
#
|
5
|
+
# The following Resources are available:
|
6
|
+
# * Account via @session.account
|
7
|
+
# * Client via @session.clients
|
8
|
+
# * Contact via @session.contacts
|
9
|
+
# * Deal via @session.deals
|
10
|
+
# * Note via @deal.notes
|
11
|
+
# * Reminder via @deal.reminders
|
12
|
+
# * Source via @deal.sources
|
13
|
+
#
|
14
|
+
# === Retrieving Resources
|
15
|
+
# For information on retrieving resources, please consult the Collection class
|
16
|
+
#
|
17
|
+
# === Creating a Resource
|
18
|
+
# To create a resource, call the create method on the appropriate collection.
|
19
|
+
#
|
20
|
+
# For example, to create a client, you would do the following:
|
21
|
+
# @client = @session.clients.create(:name => 'Google')
|
22
|
+
# This will return an instance of Pipejump::Client
|
23
|
+
#
|
24
|
+
# You can access the attributes of the instance via the attributes method:
|
25
|
+
# @client.attributes # => { 'id' => 1, 'name' => 'Google' }
|
26
|
+
#
|
27
|
+
# Pipejump::Resource also provides shortcut accessors for these attributes, similarly to ActiveRecord:
|
28
|
+
# @client.name = 'Yahoo'
|
29
|
+
# @client.name # => 'Yahoo'
|
30
|
+
#
|
31
|
+
# If the create fails, errors are available via the errors method
|
32
|
+
# @client = @session.clients.create(:name => '')
|
33
|
+
# @client.errors # => {"client"=>[{"error"=>{"code"=>"E0001", "field"=>"name", "description"=>"Please enter a client name"}}]}
|
34
|
+
#
|
35
|
+
# For more information on errors, please consult the Pipejump API documentation
|
36
|
+
#
|
37
|
+
# === Updating a Resource
|
38
|
+
# To update a resource, change the desired attributes and call the save method
|
39
|
+
# @client.name = 'Yahoo'
|
40
|
+
# @client.save
|
41
|
+
#
|
42
|
+
# The save method returns the instance on successful save.
|
43
|
+
#
|
44
|
+
# If the update fails, save returns false and errors are available via the errors method
|
45
|
+
# @client.name = ''
|
46
|
+
# @client.save # => false
|
47
|
+
# @client.errors # => {"client"=>[{"error"=>{"code"=>"E0001", "field"=>"name", "description"=>"Please enter a client name"}}]}
|
48
|
+
#
|
49
|
+
# For more information on errors, please consult the Pipejump API documentation
|
50
|
+
#
|
51
|
+
# === Removing a resource
|
52
|
+
# To remove a resource, call the destroy method on the Pipejump::Resource instance
|
53
|
+
# @client.destroy # => true
|
54
|
+
#
|
55
|
+
class Resource
|
56
|
+
|
57
|
+
class << self
|
58
|
+
# Returns the pluralized name of the resource used for the collection
|
59
|
+
def collection_path(path = nil)
|
60
|
+
@collection_path = path if path
|
61
|
+
@collection_path || "#{self.to_s.split('::').last.downcase}s"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Naive implementation of belongs_to association
|
65
|
+
def belongs_to(klass) #:nodoc:
|
66
|
+
class_eval <<-STR
|
67
|
+
def #{klass}
|
68
|
+
if @attributes['#{klass}'].is_a?(Hash)
|
69
|
+
Pipejump::#{klass.to_s.capitalize}.new(@attributes['#{klass}'].merge(:session => @session))
|
70
|
+
elsif @attributes['#{klass}_id']
|
71
|
+
@session.#{klass}s.find(@attributes['#{klass}_id'])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
STR
|
75
|
+
end
|
76
|
+
|
77
|
+
# Naive implementation of has_many association
|
78
|
+
attr_accessor :has_many_blocks #:nodoc:
|
79
|
+
def has_many(collection, &block) #:nodoc:
|
80
|
+
(self.has_many_blocks ||= {})[collection] = block
|
81
|
+
class_eval <<-STR
|
82
|
+
def #{collection}
|
83
|
+
collection = Collection.new(@session, #{collection.to_s[0..-2].capitalize}, self)
|
84
|
+
if self.class.has_many_blocks[:#{collection}]
|
85
|
+
(class << collection; self; end).class_eval(&self.class.has_many_blocks[:#{collection}])
|
86
|
+
end
|
87
|
+
collection
|
88
|
+
end
|
89
|
+
STR
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
attr_accessor :attributes
|
96
|
+
# Constructor for the Resource
|
97
|
+
def initialize(attrs)
|
98
|
+
@session = attrs.delete(:session)
|
99
|
+
@prefix = attrs.delete(:prefix) || ''
|
100
|
+
@attributes = {}
|
101
|
+
load(attrs)
|
102
|
+
end
|
103
|
+
|
104
|
+
def id
|
105
|
+
@attributes['id']
|
106
|
+
end
|
107
|
+
|
108
|
+
def load(attrs = {}) #:nodoc:
|
109
|
+
attrs.each_pair do |key, value|
|
110
|
+
@attributes[key.to_s] = value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def method_missing(meth, *args) #:nodoc:
|
115
|
+
if meth.to_s[-1].chr == '=' and @attributes[meth.to_s[0..-2]]
|
116
|
+
@attributes[meth.to_s[0..-2]] = args.first
|
117
|
+
elsif @attributes.has_key?(meth.to_s)
|
118
|
+
@attributes[meth.to_s]
|
119
|
+
else
|
120
|
+
super(meth, args)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def klassname #:nodoc:
|
125
|
+
self.class.to_s.split('::').last.downcase
|
126
|
+
end
|
127
|
+
|
128
|
+
def to_query
|
129
|
+
@attributes.collect { |pair| pair[0] = "#{klassname}[#{pair[0]}]"; pair.join('=') }.join('&')
|
130
|
+
end
|
131
|
+
|
132
|
+
def save
|
133
|
+
before_save if respond_to?(:before_save)
|
134
|
+
@errors = {}
|
135
|
+
code, data = id ? update : create # @session.post('/' + self.class.collection_path.to_s + '.json', to_query)
|
136
|
+
if data['errors']
|
137
|
+
@errors = data['errors']
|
138
|
+
false
|
139
|
+
else
|
140
|
+
load(data[klassname])
|
141
|
+
true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def inspect
|
146
|
+
"#<#{self.class} #{@attributes.collect { |pair| pair[1] = "\"#{pair[1]}\""; pair.join(': ') }.join(', ')}>"
|
147
|
+
end
|
148
|
+
|
149
|
+
def element_path
|
150
|
+
@prefix + '/' + self.class.collection_path.to_s + '/' + id.to_s
|
151
|
+
end
|
152
|
+
|
153
|
+
def collection_path
|
154
|
+
@prefix + '/' + self.class.collection_path.to_s
|
155
|
+
end
|
156
|
+
|
157
|
+
def create #:nodoc:
|
158
|
+
@session.post(collection_path + '.json', to_query)
|
159
|
+
end
|
160
|
+
|
161
|
+
def update #:nodoc:
|
162
|
+
@session.put(element_path + '.json', to_query)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Destroys the Resource
|
166
|
+
def destroy
|
167
|
+
code, data = @session.delete(element_path + '.json')
|
168
|
+
code.to_i == 200
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns a Hash of errors
|
172
|
+
def errors
|
173
|
+
@errors ||= {}
|
174
|
+
end
|
175
|
+
|
176
|
+
def created?
|
177
|
+
!!id
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|