served 0.1.12 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6caeaa76f12084ca80555e8bf472e9f85ebcae70
4
- data.tar.gz: 006b93ac7370979237e29fffe4b49a70938684fe
3
+ metadata.gz: 20a3772be5d8a3b6c27909c4cb61c4bdd33fdb2c
4
+ data.tar.gz: 3bc802236bc9a3f77bdab3577bb917313fc29332
5
5
  SHA512:
6
- metadata.gz: 9bc45c23b36761e2c2a24bb7fc295f75a807969455c02347acb74d3664de0eb865716b16da51e401bbe73a50fc1ab60fa6c898ec5e816c800d02092425770697
7
- data.tar.gz: 51e0f9ceda390a79ee3da2b1fe071ca376bf3538506d7ce6092e327e51b313200a354521b697c7b8034554c5de04db5a7b01aa33f430006a7e16dccdf52a4abc
6
+ metadata.gz: 4b9c306b2e26bd19e0fdfef7ce4abd9c14172c2490030710613c44041787e3ae07ff8dd47d51975163eefa405dd0249cd43861bb115d8df3a9ba1471517b5973
7
+ data.tar.gz: 3f45537c4946005cf8e056db0d9832e95821ba21ce8c47c2edea36582c878eeb2bf186e56b0aff7fcbd535bb6fec79dbfd8bac04a0688140193a3a24487c5aae
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.0
data/.travis.yml CHANGED
@@ -2,7 +2,8 @@ language: ruby
2
2
  rvm:
3
3
  - 2.1
4
4
  - 2.2.2
5
- - 2.3.1
5
+ - 2.3.3
6
+ - 2.4.0
6
7
  before_install: gem install bundler -v 1.10.5
7
8
 
8
9
  gemfile:
@@ -13,4 +14,4 @@ gemfile:
13
14
  matrix:
14
15
  exclude:
15
16
  - gemfile: gemfiles/5.0.gemfile
16
- rvm: 2.1
17
+ rvm: 2.1
data/CHANGELOG.md CHANGED
@@ -1,11 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ * new HTTP client backend functionality, ability to support multiple
6
+ HTTP backends
7
+ * `headers` updated to merge, can be called multiple times, allows modularization
8
+ * backend support for `HTTParty`, `HTTP`, and `Patron`
9
+ * `host_config` method removed from `Support::Resource::Base`
10
+ * update travis to test against Ruby 2.4.0, Ruby 2.1.9, Ruby 2.3.2
11
+ * various refactoring
12
+ * add `validation_on_save` option to allow skipping validation on save
13
+ * better configuration inheritance in resources
14
+ * if using HTTParty as a backend `Errno::ECONNREFUSED` will no longer be returned for connection errors,
15
+ `Served::HTTPClient::ConnectionFailed` will be raised instead
16
+
3
17
  ## 0.1.12
4
18
  * add validation support
5
19
  * add serialization support
6
20
  * allow definition of individual attributes
7
21
  * add resource level `headers` option
8
- * `host_config` will be deprecated in 0.2.0
22
+ * add resource level `reasource_name` option
23
+ * add resource level `host` option, `host_config` will be deprecated in 0.2.0
9
24
  * add `Served::Attribute::Base` class
10
25
 
11
26
  ## 0.1.11
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Served
2
- [![Build Status](https://travis-ci.org/optoro/served.svg)](https://travis-ci.org/optoro/served)
2
+ [![Build Status](https://travis-ci.org/optoro/served.svg?branch=master)](https://travis-ci.org/optoro/served)
3
3
  [![Gem Version](https://badge.fury.io/rb/served.svg)](https://badge.fury.io/rb/served)
4
4
 
5
5
  Served is a gem in the vein of [ActiveResource](https://github.com/rails/activeresource) designed to facilitate
@@ -11,6 +11,7 @@ Add the following to your Gemfile:
11
11
 
12
12
  ```gem 'served'```
13
13
 
14
+ Served supports Ruby versions `>= 2.1` and versions of Rails `>= 3.2`, including Rails 5.
14
15
  # Configuration
15
16
  Served is configured by passing a block to ```Served::configure```.
16
17
 
@@ -21,6 +22,8 @@ Served.configure do |config|
21
22
  }
22
23
 
23
24
  config.timeout = 100
25
+
26
+ config.backend = :patron
24
27
  end
25
28
  ```
26
29
 
@@ -43,6 +46,10 @@ maintained for backwards compatibility, however the extension will likely be rem
43
46
  ## Timeout
44
47
  Sets the request timeout in milliseconds.
45
48
 
49
+ ## Backend
50
+ Configure the HTTP client backend. Supports either :http (default), which will use the HTTP client, or :patron, which
51
+ will use Patron. Patron is suggested for use if high concurrency between requests is required. Also requires the
52
+ machine to have libcurl.
46
53
 
47
54
  # Defining a Resource
48
55
  A service model can be created by declaring a class inheriting from ```Service::Resource::Base```.
@@ -1,9 +1,13 @@
1
1
  module Served
2
2
  module Attribute
3
3
  class Base
4
- include Support::Attributable
5
- include Support::Serializable
6
- include Support::Validatable
4
+ include Resource::Attributable
5
+ include Resource::Serializable
6
+ include Resource::Validatable
7
+
8
+ def initialize(*args)
9
+ # placeholder
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -0,0 +1,16 @@
1
+ module Served
2
+ module Backends
3
+ class Base
4
+ delegate :headers, :resource, :template, :timeout, to: :@client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def serialize_response(response)
11
+ response
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,49 @@
1
+ require 'http'
2
+ module Served
3
+ module Backends
4
+ #HTTP Backend uses {https://github.com/httprb/http HTTP} client library.
5
+ class HTTP < Base
6
+
7
+ def get(endpoint, id, params={})
8
+ response = ::HTTP
9
+ .timeout(global: timeout)
10
+ .headers(headers)
11
+ .get(template.expand(id: id, query: params, resource: endpoint).to_s)
12
+ serialize_response(response)
13
+ rescue ::HTTP::ConnectionError
14
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
15
+ end
16
+
17
+ def put(endpoint, id, body, params={})
18
+ response = ::HTTP
19
+ .timeout(global: timeout)
20
+ .headers(headers)
21
+ .put(template.expand(id: id, query: params, resource: endpoint).to_s, body: body)
22
+ serialize_response(response)
23
+ rescue ::HTTP::ConnectionError
24
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
25
+ end
26
+
27
+ def post(endpoint, body, params={})
28
+ response = ::HTTP
29
+ .timeout(global: timeout)
30
+ .headers(headers)
31
+ .post(template.expand(query: params, resource: endpoint).to_s, body: body)
32
+ serialize_response(response)
33
+ rescue ::HTTP::ConnectionError
34
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
35
+ end
36
+
37
+ def delete(endpoint, id, params={})
38
+ response = ::HTTP
39
+ .timeout(global: timeout)
40
+ .headers(headers)
41
+ .delete(template.expand(query: params, resource: endpoint, id: id).to_s)
42
+ serialize_response(response)
43
+ rescue ::HTTP::ConnectionError
44
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,47 @@
1
+ require 'httparty'
2
+ module Served
3
+ module Backends
4
+ # HTTParty Backend uses the {https://github.com/jnunemaker/httparty HTTParty} client
5
+ class HTTParty < Base
6
+
7
+ def get(endpoint, id, params={})
8
+ ::HTTParty.get(template.expand(id: id, query: params, resource: endpoint).to_s,
9
+ headers: headers,
10
+ timeout: timeout
11
+ )
12
+ rescue Errno::ECONNREFUSED
13
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
14
+ end
15
+
16
+ def put(endpoint, id, body, params={})
17
+ ::HTTParty.put(template.expand(id: id, query: params, resource: endpoint).to_s,
18
+ body: body,
19
+ headers: headers,
20
+ timeout: timeout
21
+ )
22
+ rescue Errno::ECONNREFUSED
23
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
24
+ end
25
+
26
+ def post(endpoint, body, params={})
27
+ ::HTTParty.post(template.expand(query: params, resource: endpoint).to_s,
28
+ body: body,
29
+ headers: headers,
30
+ timeout: timeout
31
+ )
32
+ rescue Errno::ECONNREFUSED
33
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
34
+ end
35
+
36
+ def delete(endpoint, id, params={})
37
+ ::HTTParty.delete(template.expand(id: id, query: params, resource: endpoint).to_s,
38
+ headers: headers,
39
+ timeout: timeout
40
+ )
41
+ rescue Errno::ECONNREFUSED
42
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,45 @@
1
+ require 'patron'
2
+ module Served
3
+ module Backends
4
+ # Patron Backend uses {Patron https://github.com/toland/patron} for its client. This backend does not lock the GIL
5
+ # and is thread safe. Use Patron if you need high concurrency.
6
+ class Patron < Base
7
+
8
+ def get(endpoint, id, params={})
9
+ serialize_response(::Patron::Session.new(headers: headers, timeout: timeout)
10
+ .get(template.expand(id: id, query: params, resource: endpoint).to_s))
11
+ rescue ::Patron::ConnectionFailed
12
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
13
+ end
14
+
15
+ def put(endpoint, id, body, params={})
16
+ serialize_response(::Patron::Session.new(headers: headers, timeout: timeout)
17
+ .put(template.expand(id: id, query: params, resource: endpoint).to_s, body))
18
+ rescue ::Patron::ConnectionFailed
19
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
20
+ end
21
+
22
+ def post(endpoint, body, params={})
23
+ serialize_response(::Patron::Session.new(headers: headers, timeout: timeout)
24
+ .post(template.expand(query: params, resource: endpoint).to_s, body))
25
+ rescue ::Patron::ConnectionFailed
26
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
27
+ end
28
+
29
+ def delete(endpoint, id, params={})
30
+ serialize_response(::Patron::Session.new(headers: headers, timeout: timeout)
31
+ .delete(template.expand(id: id, query: params, resource: endpoint).to_s))
32
+ rescue ::Patron::ConnectionFailed
33
+ raise Served::HTTPClient::ConnectionFailed.new(resource)
34
+ end
35
+
36
+ def serialize_response(response)
37
+ OpenStruct.new({
38
+ body: response.body,
39
+ code: response.status
40
+ })
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'backends/base'
2
+ module Served
3
+ module Backends
4
+
5
+ # @private
6
+ def self.[](backend)
7
+ @backends ||= {
8
+ http: 'HTTP',
9
+ patron: 'Patron',
10
+ httparty: 'HTTParty'
11
+ }
12
+ if @backends[backend]
13
+ require_relative "backends/#{backend}"
14
+ return self.const_get(@backends[backend].classify.to_sym)
15
+ end
16
+ require_relative 'backends/httparty'
17
+ self.const_get(@backends[:httparty].classify.to_sym)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ module Served
2
+ include ActiveSupport::Configurable
3
+ config_accessor :timeout
4
+ config_accessor :backend
5
+
6
+ configure do |config|
7
+ config.timeout = 30
8
+ config.backend = :http
9
+ config.hosts = {}
10
+ end
11
+
12
+ end
@@ -1,38 +1,29 @@
1
1
  require 'addressable/template'
2
2
  module Served
3
- # Provides an interface between HTTParty and the models. Most of the crap in here is self explanatory
3
+ # Provides an interface between the HTTP client and the Resource.
4
4
  class HTTPClient
5
- HEADERS = { 'Content-type' => 'application/json', 'Accept' => 'application/json' }
5
+
6
6
  DEFAULT_TEMPLATE = '{/resource*}{/id}.json{?query*}'
7
7
 
8
- def initialize(host, timeout, headers={})
9
- host += DEFAULT_TEMPLATE unless host =~ /{.+}/
10
- @template = Addressable::Template.new(host)
11
- @timeout = timeout
12
- @headers = HEADERS.merge(headers || {})
13
- end
8
+ attr_reader :template, :resource
14
9
 
15
- def get(endpoint, id, params={})
16
- HTTParty.get(@template.expand(id: id, query: params, resource: endpoint).to_s,
17
- headers: @headers,
18
- timeout: @timeout
19
- )
20
- end
10
+ delegate :get, :put, :delete, :post, to: :@backend
11
+ delegate :headers, :timeout, :host, to: :@resource
21
12
 
22
- def put(endpoint, id, body, params={})
23
- HTTParty.put(@template.expand(id: id, query: params, resource: endpoint).to_s,
24
- body: body,
25
- headers: @headers,
26
- timeout: @timeout
27
- )
28
- end
13
+ class ConnectionFailed < StandardError
29
14
 
30
- def post(endpoint, body, params={})
31
- HTTParty.post(@template.expand(query: params, resource: endpoint).to_s,
32
- body: body,
33
- headers: @headers,
34
- timeout: @timeout
35
- )
15
+ def initialize(resource)
16
+ super "Resource #{resource.name} could not be reached on #{resource.host}"
17
+ end
18
+
19
+ end
20
+
21
+ def initialize(resource)
22
+ @resource = resource
23
+ h = host + @resource.template
24
+ @template = Addressable::Template.new(h)
25
+ @backend = Served::Backends[Served.config.backend].new(self)
36
26
  end
27
+
37
28
  end
38
29
  end
@@ -1,8 +1,13 @@
1
1
  module Served
2
- module Support
2
+ module Resource
3
3
  module Attributable
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ included do
7
+ prepend Prepend
8
+ singleton_class.prepend ClassMethods::Prepend
9
+ end
10
+
6
11
  module ClassMethods
7
12
 
8
13
  # declare an attribute for the resource
@@ -33,10 +38,31 @@ module Served
33
38
  @attributes ||= {}
34
39
  end
35
40
 
41
+ module Prepend
42
+
43
+ def inherited(subclass)
44
+ self.attributes.each do |name, options|
45
+ subclass.attribute name, options
46
+ end
47
+ super
48
+ end
49
+
50
+ end
51
+
36
52
  end
37
53
 
38
- def initialize(options={})
39
- reload_with_attributes(options)
54
+ module Prepend
55
+
56
+ def initialize(options={})
57
+ reload_with_attributes(options.symbolize_keys)
58
+ super options
59
+ end
60
+
61
+ end
62
+
63
+ # @return [Array] the keys for all the defined attributes
64
+ def attributes
65
+ Hash[self.class.attributes.keys.collect { |name| [name, send(name)] }]
40
66
  end
41
67
 
42
68
  private
@@ -56,12 +82,10 @@ module Served
56
82
  end
57
83
 
58
84
  def set_attribute(name, value)
59
- raise InvalidAttributeError, "`#{name}' is not a valid attribute" unless self.class.attributes.include?(name)
60
85
  instance_variable_set("@#{name}", value)
61
86
  end
62
87
 
63
88
  end
64
89
 
65
-
66
90
  end
67
- end
91
+ end
@@ -1,3 +1,8 @@
1
+ require_relative 'attributable'
2
+ require_relative 'serializable'
3
+ require_relative 'validatable'
4
+ require_relative 'configurable'
5
+
1
6
  module Served
2
7
  module Resource
3
8
  # Service Resources should inherit directly from this class. Provides interfaces necessary for communicating with
@@ -12,34 +17,64 @@ module Served
12
17
  # A resource may also serialize values as specific classes, including nested resources. If serialize is set to a
13
18
  # Served Resource, it will validate the nested resource as well as the top level.
14
19
  class Base
15
- include Support::Attributable
16
- include Support::Validatable
17
- include Support::Serializable
20
+ include Configurable
21
+ include Attributable
22
+ include Validatable
23
+ include Serializable
24
+
25
+ attribute :id
26
+
27
+ # Default headers for every request
28
+ HEADERS = {'Content-type' => 'application/json', 'Accept' => 'application/json'}
18
29
 
19
- # raised when an attribute is passed to a resource that is not declared
20
- class InvalidAttributeError < StandardError;
21
- end
22
30
 
23
31
  # raised when the connection receives a response from a service that does not constitute a 200
24
32
  class ServiceError < StandardError
25
33
  attr_reader :response
26
34
 
27
- def initialize(response)
35
+ def initialize(resource, response)
28
36
  @response = response
29
- super "An error occurred making the request: #{@response.code}"
37
+ begin
38
+ error = JSON.parse(response.body)
39
+ rescue JSON::ParserError
40
+ super "Service #{resource.class.name} experienced an error and sent back an invalid error response"
41
+ return
42
+ end
43
+ super "Service #{resource.class.name} responded with an error: #{error['error']} -> #{error['exception']}"
44
+ set_backtrace(error['traces']['Full Trace'].collect {|e| e['trace']})
30
45
  end
31
46
  end
32
47
 
33
- class << self
48
+ class_configurable :resource_name do
49
+ name.split('::').last.tableize
50
+ end
51
+
52
+ class_configurable :host do
53
+ Served.config[:hosts][parent.name.underscore.split('/')[-1]] || Served.config[:hosts][:default]
54
+ end
34
55
 
56
+ class_configurable :timeout do
57
+ Served.config.timeout
58
+ end
59
+
60
+ class_configurable :_headers do
61
+ HEADERS
62
+ end
63
+
64
+ class_configurable :template do
65
+ '{/resource*}{/id}.json{?query*}'
66
+ end
67
+
68
+ class << self
35
69
 
36
70
  # Defines the default headers that should be used for the request.
37
71
  #
38
72
  # @param headers [Hash] the headers to send with each requesat
39
73
  # @return headers [Hash] the default headers for the class
40
74
  def headers(h={})
41
- @headers = h unless h.empty?
42
- @headers
75
+ headers ||= _headers
76
+ _headers(headers.merge!(h)) unless h.empty?
77
+ _headers
43
78
  end
44
79
 
45
80
  # Looks up a resource on the service by id. For example `SomeResource.find(5)` would call `/some_resources/5`
@@ -51,62 +86,21 @@ module Served
51
86
  instance.reload
52
87
  end
53
88
 
54
- # Get or set the resource name for the given resource used for endpoint generation
55
- #
56
- # @param resource [String] the name of the resource
57
- # @return [String] the name of the resource. `SomeResource.resource_name` will return `some_resources`
58
- def resource_name(resource=nil)
59
- @resource_name = resource if resource
60
- @resource_name ||name.split('::').last.tableize
61
- end
62
-
63
- # @deprecated returns host information
64
- def host_config
65
- host
66
- end
67
-
68
- # Get or set the host for the resource
69
- #
70
- # @param host [String] the resource host
71
- # @return [String] or [Hash] the configured host.
72
- # @see Services::Configuration
73
- def host(h=nil)
74
- @host = h if h
75
- @host ||= Served.config[:hosts][parent.name.underscore.split('/')[-1]]
76
- end
77
-
78
- # Get or set the timeout for the current resource
79
- #
80
- # @return [Integer] allowed timeout in seconds
81
- def timeout(sec=nil)
82
- @timeout = sec if sec
83
- @timeout || Served.config.timeout
84
- end
85
-
89
+ # @return [Served::HTTPClient] the HTTPClient using the configured backend
86
90
  def client
87
- @connection ||= Served::HTTPClient.new(host_config, timeout, headers)
88
- end
89
-
90
- private
91
-
92
- # Everything should allow an id attribute
93
- def inherited(subclass)
94
- return if subclass.attributes.include?(:id) # attribute method does this already, but rather not do a
95
- # class_eval if not necessary
96
- subclass.class_eval do
97
- attribute :id
98
- end
91
+ @client ||= Served::HTTPClient.new(self)
99
92
  end
100
93
 
101
94
  end
102
95
 
103
- # @see Services::Resource::Base::resource_name
104
- def resource_name
105
- self.class.resource_name
96
+ def initialize(options={})
97
+ # placeholder
106
98
  end
107
99
 
108
100
  # Saves the record to the service. Will call POST if the record does not have an id, otherwise will call PUT
109
101
  # to update the record
102
+ #
103
+ # @return [Boolean] returns true or false depending on save success
110
104
  def save
111
105
  if id
112
106
  reload_with_attributes(put[resource_name.singularize])
@@ -116,13 +110,9 @@ module Served
116
110
  true
117
111
  end
118
112
 
119
- alias_method :save!, :save # TODO: differentiate save! and safe much the same AR does.
120
-
121
- def attributes
122
- Hash[self.class.attributes.keys.collect { |name| [name, send(name)] }]
123
- end
124
-
125
113
  # Reloads the resource using attributes from the service
114
+ #
115
+ # @return [self] self
126
116
  def reload
127
117
  reload_with_attributes(get)
128
118
  self
@@ -145,7 +135,7 @@ module Served
145
135
  end
146
136
 
147
137
  def handle_response(response)
148
- raise ServiceError, response unless (200..299).include?(response.code)
138
+ raise ServiceError.new(self, response) unless (200..299).include?(response.code)
149
139
  JSON.parse(response.body)
150
140
  end
151
141
 
@@ -153,6 +143,10 @@ module Served
153
143
  self.class.client
154
144
  end
155
145
 
146
+ def presenter
147
+ {resource_name.singularize.to_sym => attributes}
148
+ end
149
+
156
150
  end
157
151
  end
158
152
  end
@@ -0,0 +1,52 @@
1
+ module Served
2
+ module Resource
3
+ module Configurable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ singleton_class.prepend ClassMethods::Prepend
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ module Prepend
13
+
14
+ private
15
+
16
+ def inherited(subclass)
17
+ super
18
+ instance_variables.each do |v|
19
+ instance = instance_variable_get(v)
20
+ instance = instance.clone unless instance.is_a? Fixnum
21
+ subclass.send(:instance_variable_set, v, instance) if /@_c_/ =~ v
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ private
28
+
29
+ # Declare a configurable attribute. This is used to declare the configuration methods used in
30
+ # Served::Resource::Base
31
+ def class_configurable(name, options={}, &block)
32
+ instance_eval do
33
+ instance_variable_set(:"@_c_#{name}", options[:default]) if options[:default]
34
+ instance_variable_set(:"@_c_#{name}", block ) if block_given? && !instance_variable_get(:"@#{name}")
35
+
36
+ define_singleton_method(name) do |value=nil|
37
+ instance_variable_set(:"@_c_#{name}", value) if value
38
+ value = instance_variable_get(:"@_c_#{name}") unless value
39
+ return instance_eval &value if value.is_a? Proc
40
+ value
41
+ end
42
+
43
+ define_method(name) do
44
+ self.class.send(name)
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,14 +1,29 @@
1
1
  module Served
2
- module Support
2
+ module Resource
3
3
  module Serializable
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ # pseudo boolean class for serialization
7
+ unless Object.const_defined?(:Boolean)
8
+ class ::Boolean;
9
+ end
10
+ end
11
+
6
12
  # Specialized class serializers
7
13
  SERIALIZERS = {
8
14
  Fixnum => {call: :to_i},
9
15
  String => {call: :to_s},
10
- Symbol => {call: :to_sym},
11
- Float => {call: :to_f}
16
+ Symbol => {call: :to_sym, converter: -> (value) {
17
+ if value.is_a? Array
18
+ value = value.map { |a| a.to_sym }
19
+ return value
20
+ end
21
+ }},
22
+ Float => {call: :to_f},
23
+ Boolean => {converter: -> (value) {
24
+ return false unless value == "true"
25
+ true
26
+ }}
12
27
  }
13
28
 
14
29
  included do
@@ -27,8 +42,12 @@ module Served
27
42
  if serializer.is_a? Proc
28
43
  value = serializer.call(value)
29
44
  elsif s = SERIALIZERS[serializer]
30
- value = value.send(s[:call]) if s[:call] && value.respond_to?(s[:call])
31
- value = s[:converter].call(value) if s[:converter]
45
+ called = false
46
+ if s[:call] && value.respond_to?(s[:call])
47
+ value = value.send(s[:call])
48
+ called = true
49
+ end
50
+ value = s[:converter].call(value) if s[:converter] && !called
32
51
  else
33
52
  value = serializer.new(value)
34
53
  end
@@ -36,7 +55,6 @@ module Served
36
55
  super
37
56
  end
38
57
 
39
-
40
58
  end
41
59
 
42
60
  module ClassMethods
@@ -50,7 +68,7 @@ module Served
50
68
  end
51
69
 
52
70
  # renders the model as json
53
- def to_json
71
+ def to_json(*args)
54
72
  raise InvalidPresenter, 'Presenter must respond to #to_json' unless presenter.respond_to? :to_json
55
73
  presenter.to_json
56
74
  end
@@ -58,7 +76,7 @@ module Served
58
76
  # override this to return a presenter to be used for serialization, otherwise all attributes will be
59
77
  # serialized
60
78
  def presenter
61
- {resource_name.singularize => attributes}
79
+ attributes
62
80
  end
63
81
 
64
82
  end
@@ -1,5 +1,5 @@
1
1
  module Served
2
- module Support
2
+ module Resource
3
3
  # Resource validation functionality
4
4
  module Validatable
5
5
  extend ActiveSupport::Concern
@@ -10,23 +10,40 @@ module Served
10
10
  :numericality,
11
11
  :format,
12
12
  :inclusion,
13
- :confirmation
14
13
  ]
15
14
 
15
+ class ResourceInvalid < StandardError
16
+
17
+ def initialize(resource)
18
+ super "[#{resource.errors.full_messages.join(', ')}]"
19
+ end
20
+ end
21
+
22
+ # Saves a resource and raises an error if the save fails.
23
+ def save!
24
+ raise ResourceInvalid.new(self) unless run_validations! && save(false)
25
+ true
26
+ end
27
+
16
28
  included do
17
29
  include ActiveModel::Validations
30
+ include Configurable
31
+ include Attributable
18
32
  singleton_class.prepend ClassMethods::Prepend
19
33
  prepend Prepend
34
+
35
+ class_configurable :validate_on_save do
36
+ true
37
+ end
20
38
  end
21
39
 
22
40
  module Prepend
23
41
 
24
- def save
25
- return false unless valid?
26
- super
42
+ def save(with_validations=true)
43
+ return false if (with_validations && self.class.validate_on_save && !valid?)
44
+ super()
27
45
  end
28
46
 
29
-
30
47
  protected
31
48
 
32
49
  def run_validations!
@@ -41,6 +58,7 @@ module Served
41
58
  end
42
59
 
43
60
  module ClassMethods
61
+
44
62
  module Prepend
45
63
 
46
64
  def attribute(name, options={})
@@ -63,4 +81,4 @@ module Served
63
81
 
64
82
  end
65
83
  end
66
- end
84
+ end
@@ -1 +1 @@
1
- require 'served/resource/base'
1
+ require 'served/resource/base'
@@ -1,3 +1,3 @@
1
1
  module Served
2
- VERSION = '0.1.12'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/served.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'httparty'
2
1
  require 'active_support/configurable'
3
2
  require 'active_support/core_ext/string'
4
3
  require 'active_support/core_ext/module'
@@ -6,14 +5,11 @@ require 'active_model'
6
5
 
7
6
  require 'served/engine'
8
7
  require 'served/version'
8
+ require 'served/config'
9
+ require 'served/backends'
9
10
  require 'served/http_client'
10
- require 'served/support'
11
11
  require 'served/resource'
12
12
  require 'served/attribute'
13
13
 
14
- module Served
15
- include ActiveSupport::Configurable
16
- configure do |config|
17
- config.timeout = 30
18
- end
19
- end
14
+
15
+
data/served.gemspec CHANGED
@@ -19,12 +19,15 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ['lib']
21
21
 
22
- spec.add_dependency 'httparty'
23
22
  spec.add_dependency 'activesupport', '>= 3.2'
24
23
  spec.add_dependency 'addressable', '>= 2.4.0'
25
24
  spec.add_dependency 'activemodel', '>= 3.2'
26
25
 
27
- spec.add_development_dependency 'bundler', '~> 1.10'
28
- spec.add_development_dependency 'rake', '~> 10.0'
29
- spec.add_development_dependency 'rspec'
26
+ spec.add_development_dependency 'httparty', '~> 0.14.0'
27
+ spec.add_development_dependency 'bundler', '~> 1.10'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.4.0'
30
+ spec.add_development_dependency 'http', '~> 1.0.4'
31
+ spec.add_development_dependency 'patron', '~> 0.5.0'
32
+
30
33
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: served
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jarod Reid
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-06 00:00:00.000000000 Z
11
+ date: 2017-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: httparty
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: activesupport
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +52,20 @@ dependencies:
66
52
  - - ">="
67
53
  - !ruby/object:Gem::Version
68
54
  version: '3.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: httparty
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.14.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.14.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: bundler
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -98,16 +98,44 @@ dependencies:
98
98
  name: rspec
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 3.4.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 3.4.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: http
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.0.4
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.0.4
125
+ - !ruby/object:Gem::Dependency
126
+ name: patron
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.5.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
109
137
  - !ruby/object:Gem::Version
110
- version: '0'
138
+ version: 0.5.0
111
139
  description: Served provides an easy to use model layer for communicating with disributed
112
140
  Rails based Services.
113
141
  email:
@@ -118,6 +146,7 @@ extra_rdoc_files: []
118
146
  files:
119
147
  - ".gitignore"
120
148
  - ".rspec"
149
+ - ".ruby-version"
121
150
  - ".travis.yml"
122
151
  - CHANGELOG.md
123
152
  - CODE_OF_CONDUCT.md
@@ -133,14 +162,20 @@ files:
133
162
  - lib/served.rb
134
163
  - lib/served/attribute.rb
135
164
  - lib/served/attribute/base.rb
165
+ - lib/served/backends.rb
166
+ - lib/served/backends/base.rb
167
+ - lib/served/backends/http.rb
168
+ - lib/served/backends/httparty.rb
169
+ - lib/served/backends/patron.rb
170
+ - lib/served/config.rb
136
171
  - lib/served/engine.rb
137
172
  - lib/served/http_client.rb
138
173
  - lib/served/resource.rb
174
+ - lib/served/resource/attributable.rb
139
175
  - lib/served/resource/base.rb
140
- - lib/served/support.rb
141
- - lib/served/support/attributable.rb
142
- - lib/served/support/serializable.rb
143
- - lib/served/support/validatable.rb
176
+ - lib/served/resource/configurable.rb
177
+ - lib/served/resource/serializable.rb
178
+ - lib/served/resource/validatable.rb
144
179
  - lib/served/version.rb
145
180
  - served.gemspec
146
181
  homepage: http://github.com/fugufish/served
@@ -163,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
198
  version: '0'
164
199
  requirements: []
165
200
  rubyforge_project:
166
- rubygems_version: 2.6.10
201
+ rubygems_version: 2.6.8
167
202
  signing_key:
168
203
  specification_version: 4
169
204
  summary: Served provides an easy to use model layer for communicating with disributed
@@ -1,3 +0,0 @@
1
- require_relative 'support/attributable'
2
- require_relative 'support/serializable'
3
- require_relative 'support/validatable'