acfs 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.
Files changed (45) hide show
  1. data/.gitignore +0 -1
  2. data/.travis.yml +1 -3
  3. data/{LICENSE.txt → LICENSE} +0 -0
  4. data/README.md +45 -7
  5. data/acfs.gemspec +3 -0
  6. data/gemfiles/{Gemfile.rails-3-1 → Gemfile.rails-4-0} +2 -2
  7. data/lib/acfs.rb +49 -5
  8. data/lib/acfs/adapter/typhoeus.rb +42 -0
  9. data/lib/acfs/collection.rb +22 -0
  10. data/lib/acfs/middleware/base.rb +21 -0
  11. data/lib/acfs/middleware/json_decoder.rb +15 -0
  12. data/lib/acfs/middleware/print.rb +23 -0
  13. data/lib/acfs/model.rb +25 -12
  14. data/lib/acfs/{attributes.rb → model/attributes.rb} +36 -40
  15. data/lib/acfs/model/attributes/integer.rb +18 -0
  16. data/lib/acfs/model/attributes/string.rb +18 -0
  17. data/lib/acfs/{initialization.rb → model/initialization.rb} +4 -1
  18. data/lib/acfs/model/loadable.rb +18 -0
  19. data/lib/acfs/model/locatable.rb +24 -0
  20. data/lib/acfs/model/query_methods.rb +66 -0
  21. data/lib/acfs/model/relations.rb +10 -0
  22. data/lib/acfs/model/service.rb +29 -0
  23. data/lib/acfs/request.rb +33 -0
  24. data/lib/acfs/request/callbacks.rb +46 -0
  25. data/lib/acfs/response.rb +21 -0
  26. data/lib/acfs/response/formats.rb +12 -0
  27. data/lib/acfs/service.rb +30 -0
  28. data/lib/acfs/version.rb +1 -1
  29. data/spec/acfs/middleware/json_decoder_spec.rb +45 -0
  30. data/spec/acfs/request/callbacks_spec.rb +34 -0
  31. data/spec/acfs/request_spec.rb +71 -0
  32. data/spec/acfs_spec.rb +69 -0
  33. data/spec/{attributes_spec.rb → model/attributes_spec.rb} +25 -12
  34. data/spec/model/initialization_spec.rb +30 -0
  35. data/spec/model/locatable_spec.rb +15 -0
  36. data/spec/service_spec.rb +37 -0
  37. data/spec/spec_helper.rb +2 -0
  38. data/spec/support/service.rb +24 -0
  39. metadata +89 -23
  40. data/lib/acfs/attributes/integer.rb +0 -15
  41. data/lib/acfs/attributes/string.rb +0 -15
  42. data/lib/acfs/client.rb +0 -15
  43. data/spec/client_spec.rb +0 -12
  44. data/spec/initialization_spec.rb +0 -22
  45. data/spec/support/my_client.rb +0 -12
data/.gitignore CHANGED
@@ -17,5 +17,4 @@ test/version_tmp
17
17
  tmp
18
18
  .idea
19
19
  *.iml
20
- .rakeTasks
21
20
  .rvmrc
data/.travis.yml CHANGED
@@ -2,9 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 2.0.0
4
4
  - 1.9.3
5
- - jruby
6
5
 
7
6
  gemfile:
8
- - gemfiles/Gemfile.rails-3-1
9
7
  - gemfiles/Gemfile.rails-3-2
10
- - gemfiles/Gemfile.rails-head
8
+ - gemfiles/Gemfile.rails-4-0
File without changes
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Acfs - *API Client for Services*
1
+ # Acfs - *API client for services*
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/acfs.png)](http://badge.fury.io/rb/acfs) [![Build Status](https://travis-ci.org/jgraichen/acfs.png?branch=master)](https://travis-ci.org/jgraichen/acfs) [![Coverage Status](https://coveralls.io/repos/jgraichen/acfs/badge.png?branch=master)](https://coveralls.io/r/jgraichen/acfs) [![Code Climate](https://codeclimate.com/github/jgraichen/acfs.png)](https://codeclimate.com/github/jgraichen/acfs) [![Dependency Status](https://gemnasium.com/jgraichen/acfs.png)](https://gemnasium.com/jgraichen/acfs)
4
4
 
@@ -10,7 +10,10 @@ TODO: Write a gem description
10
10
 
11
11
  Add this line to your application's Gemfile:
12
12
 
13
- gem 'acfs'
13
+ gem 'acfs', '0.3.0'
14
+
15
+ **Note:** Acfs is under development. API may change anytime. No semantic versioning until version `1.0`. Version `1.0`
16
+ does not mean complete feature set but stable basic code base.
14
17
 
15
18
  And then execute:
16
19
 
@@ -22,13 +25,11 @@ Or install it yourself as:
22
25
 
23
26
  ## Usage
24
27
 
25
- TODO: Write usage instructions here
26
-
27
- ### Acfs::Attributes
28
+ ### Attributes
28
29
 
29
30
  ```ruby
30
31
  class MyModel
31
- include Acfs::Attributes
32
+ include Acfs::Model
32
33
 
33
34
  attribute :name, :string
34
35
  attribute :age, :integer, default: 15
@@ -41,12 +42,43 @@ mo.name # => "Johnny"
41
42
  mo.age = '13'
42
43
  mo.age # => 13
43
44
  mo.attributes # => { "name" => "Johnny", "age" => 13 }
45
+ ```
46
+
47
+ ### Service, Model & Collection
48
+
49
+ ```ruby
50
+ class MyService < Acfs::Service
51
+ self.base_url = 'http://acc.srv'
52
+ end
53
+
54
+ class User
55
+ include Acfs::Model
56
+ service MyService
57
+
58
+ attribute :id, :integer
59
+ end
44
60
 
61
+ @user = User.find 14
62
+
63
+ @user.loaded? #=> false
64
+
65
+ Acfs.run # This will run all queued request as parallel as possible.
66
+ # For @user the following URL will be requested:
67
+ # `http://acc.srv/users/14`
68
+
69
+ @model.name # => "..."
70
+
71
+ @users = User.all
72
+ @users.loaded? #=> false
73
+
74
+ Acfs.run # Will request `http://acc.srv/users`
75
+
76
+ @users #=> [<User>, ...]
45
77
  ```
46
78
 
47
79
  ## TODO
48
80
 
49
- * Library Code
81
+ * Develop Library
50
82
  * Documentation
51
83
 
52
84
  ## Contributing
@@ -58,3 +90,9 @@ mo.attributes # => { "name" => "Johnny", "age" => 13 }
58
90
  5. Commit your changes (`git commit -am 'Add some feature'`)
59
91
  6. Push to the branch (`git push origin my-new-feature`)
60
92
  7. Create new Pull Request
93
+
94
+ ## License
95
+
96
+ MIT License
97
+
98
+ Copyright (c) 2013 Jan Graichen. MIT license, see LICENSE for more details.
data/acfs.gemspec CHANGED
@@ -20,8 +20,11 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_runtime_dependency 'activesupport'
22
22
  spec.add_runtime_dependency 'activemodel'
23
+ spec.add_runtime_dependency 'multi_json'
24
+ spec.add_runtime_dependency 'typhoeus'
23
25
 
24
26
  spec.add_development_dependency 'bundler', '~> 1.3'
27
+ spec.add_development_dependency 'webmock', '~> 1.7'
25
28
  spec.add_development_dependency 'rake'
26
29
  spec.add_development_dependency 'rspec'
27
30
  spec.add_development_dependency 'guard-rspec'
@@ -3,5 +3,5 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in acfs.gemspec
4
4
  gemspec path: '../'
5
5
 
6
- gem 'activesupport', '~> 3.1.0'
7
- gem 'activemodel', '~> 3.1.0'
6
+ gem 'activesupport', '~> 4.0.0.beta1'
7
+ gem 'activemodel', '~> 4.0.0.beta1'
data/lib/acfs.rb CHANGED
@@ -1,14 +1,58 @@
1
1
  require 'active_support'
2
+ require 'active_support/core_ext'
2
3
  require 'acfs/version'
3
4
 
4
5
  module Acfs
5
6
  extend ActiveSupport::Autoload
6
7
 
7
- autoload :Client
8
- autoload :Base
9
-
8
+ autoload :Collection
10
9
  autoload :Model
11
- autoload :Attributes
12
- autoload :Initialization
10
+ autoload :Request
11
+ autoload :Response
12
+ autoload :Service
13
+
14
+ module Middleware
15
+ extend ActiveSupport::Autoload
16
+
17
+ autoload :Base
18
+ autoload :Print
19
+ autoload :JsonDecoder
20
+ end
21
+
22
+ module Adapter
23
+ extend ActiveSupport::Autoload
24
+
25
+ autoload :Typhoeus
26
+ end
27
+
28
+ class << self
29
+
30
+ # Run all queued
31
+ def run
32
+ adapter.run
33
+ end
34
+
35
+ def queue(req, &block)
36
+ request = middleware.call Request.new(req)
37
+ request.on_complete &block if block_given?
38
+ adapter.queue request
39
+ end
40
+
41
+ def adapter
42
+ @adapter ||= Adapter::Typhoeus.new
43
+ end
44
+
45
+ def middleware
46
+ @middleware ||= lambda { |request| request }
47
+ end
48
+
49
+ def use(klass, options = {})
50
+ @middlewares ||= []
51
+
52
+ return false if @middlewares.include? klass
13
53
 
54
+ @middlewares << klass
55
+ @middleware = klass.new(middleware, options)
56
+ end
57
+ end
14
58
  end
@@ -0,0 +1,42 @@
1
+ require 'typhoeus'
2
+
3
+ module Acfs
4
+ module Adapter
5
+
6
+ # Adapter for Typhoeus.
7
+ #
8
+ class Typhoeus
9
+
10
+ # Run all queued requests.
11
+ #
12
+ def run
13
+ hydra.run
14
+ end
15
+
16
+ # Add a new request or URL to the queue.
17
+ #
18
+ def queue(req)
19
+ hydra.queue convert_request(req)
20
+ end
21
+
22
+ protected
23
+ def hydra
24
+ @hydra ||= ::Typhoeus::Hydra.new
25
+ end
26
+
27
+ def convert_request(req)
28
+ request = ::Typhoeus::Request.new req.url, params: req.params, headers: req.headers, body: req.body
29
+
30
+ request.on_complete do |response|
31
+ req.complete! convert_response(req, response)
32
+ end
33
+
34
+ request
35
+ end
36
+
37
+ def convert_response(request, response)
38
+ Acfs::Response.new(request, response.code, response.headers, response.body)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ require 'delegate'
2
+
3
+ require 'acfs/model/loadable'
4
+
5
+ module Acfs
6
+
7
+ class Collection < ::Delegator
8
+ include Model::Loadable
9
+
10
+ def initialize
11
+ super([])
12
+ end
13
+
14
+ def __getobj__
15
+ @models
16
+ end
17
+
18
+ def __setobj__(obj)
19
+ @models = obj
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ module Acfs
2
+ module Middleware
3
+
4
+ # A base middleware that does not modify request or response.
5
+ # Can be used as super class for custom middleware implementations.
6
+ #
7
+ class Base
8
+ attr_reader :app, :options
9
+
10
+ def initialize(app, options = {})
11
+ @app = app
12
+ @options = options
13
+ end
14
+
15
+ def call(request)
16
+ request.on_complete { |res| response(res) } if respond_to? :response
17
+ app.call(request)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ require 'multi_json'
2
+
3
+ module Acfs
4
+ module Middleware
5
+
6
+ # A middleware to automatically decode JSON responses.
7
+ #
8
+ class JsonDecoder < Base
9
+
10
+ def response(response)
11
+ response.data = ::MultiJson.load(response.body) if response.json?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module Acfs
2
+ module Middleware
3
+
4
+ # Print resquests and response on terminal
5
+ #
6
+ class Print < Base
7
+
8
+ def call(req)
9
+ puts '-' * 80
10
+ puts req.inspect
11
+ puts '-' * 80
12
+
13
+ super
14
+ end
15
+
16
+ def response(res)
17
+ puts '-' * 80
18
+ puts res.inspect
19
+ puts '-' * 80
20
+ end
21
+ end
22
+ end
23
+ end
data/lib/acfs/model.rb CHANGED
@@ -1,22 +1,35 @@
1
1
  require 'active_model'
2
2
 
3
+ require 'acfs/model/attributes'
4
+ require 'acfs/model/loadable'
5
+ require 'acfs/model/locatable'
6
+ require 'acfs/model/query_methods'
7
+ require 'acfs/model/relations'
8
+ require 'acfs/model/service'
9
+
3
10
  module Acfs
4
11
  module Model
5
- def self.included(base)
6
- base.class_eval do
7
- if ActiveModel::VERSION::MAJOR >= 4
8
- include ActiveModel::Model
9
- else
10
- extend ActiveModel::Naming
11
- extend ActiveModel::Translation
12
- include ActiveModel::Conversion
13
- include ActiveModel::Validations
12
+ extend ActiveSupport::Concern
14
13
 
15
- include Acfs::Initialization
16
- end
14
+ included do
15
+ if ActiveModel::VERSION::MAJOR >= 4
16
+ include ActiveModel::Model
17
+ else
18
+ extend ActiveModel::Naming
19
+ extend ActiveModel::Translation
20
+ include ActiveModel::Conversion
21
+ include ActiveModel::Validations
17
22
 
18
- include Acfs::Attributes
23
+ require 'acfs/model/initialization'
24
+ include Model::Initialization
19
25
  end
26
+
27
+ include Model::Attributes
28
+ include Model::Loadable
29
+ include Model::Locatable
30
+ include Model::QueryMethods
31
+ include Model::Relations
32
+ include Model::Service
20
33
  end
21
34
  end
22
35
  end
@@ -1,12 +1,11 @@
1
- module Acfs
1
+ module Acfs::Model
2
2
 
3
- # == Acfs Attribute
3
+ # == Acfs Attributes
4
4
  #
5
5
  # Allows to specify attributes of a class with default values and type safety.
6
6
  #
7
7
  # class User
8
- # include Acfs::Attributes
9
- #
8
+ # include Acfs::Model
10
9
  # attribute :name, :string, default: 'Anon'
11
10
  # attribute :age, :integer
12
11
  # attribute :special, My::Special::Type
@@ -16,30 +15,31 @@ module Acfs
16
15
  # type casted when set.
17
16
  #
18
17
  module Attributes
19
- def self.included(base) # :nodoc:
20
- base.class_eval do
21
- extend ClassMethods
22
- include InstanceMethods
23
- end
18
+ extend ActiveSupport::Concern
19
+
20
+ def initialize(*attrs) # :nodoc:
21
+ self.class.attributes.each { |k, v| public_send :"#{k}=", v }
22
+ super *attrs
24
23
  end
25
24
 
26
- module InstanceMethods # :nodoc:
25
+ # Returns ActiveModel compatible list of attributes and values.
26
+ #
27
+ # class User
28
+ # include Acfs::Model
29
+ # attribute :name, type: String, default: 'Anon'
30
+ # end
31
+ # user = User.new(name: 'John')
32
+ # user.attributes # => { "name" => "John" }
33
+ #
34
+ def attributes
35
+ self.class.attributes.keys.inject({}) { |h, k| h[k.to_s] = public_send k; h }
36
+ end
27
37
 
28
- def initialize(*attrs) # :nodoc:
29
- self.class.attributes.each { |k, v| send :"#{k}=", v }
30
- super
31
- end
32
-
33
- # Returns ActiveModel compatible list of attributes and values.
34
- #
35
- # class User
36
- # attribute :name, type: String, default: 'Anon'
37
- # end
38
- # user = User.new(name: 'John')
39
- # user.attributes # => { "name" => "John" }
40
- #
41
- def attributes
42
- self.class.attributes.keys.inject({}) { |h, k| h[k.to_s] = send k; h }
38
+ # Update all attributes with given hash.
39
+ #
40
+ def attributes=(attributes)
41
+ self.attributes.each do |key, _|
42
+ send :"#{key}=", attributes[key] if attributes.has_key? key
43
43
  end
44
44
  end
45
45
 
@@ -49,29 +49,26 @@ module Acfs
49
49
  # setter for given attribute name. Existing methods will be overridden.
50
50
  #
51
51
  # class User
52
- # attribute :name, type: String, default: 'Anon'
52
+ # include Acfs::Model
53
+ # attribute :name, :string, default: 'Anon'
53
54
  # end
54
55
  #
55
- # Available types can be found in `Acfs::Attributes::*`.
56
+ # Available types can be found in `Acfs::Model::Attributes::*`.
56
57
  #
57
- def attribute(*attrs)
58
- opts = attrs.extract_options!
59
- type = opts.delete(:type) || :string
60
-
58
+ def attribute(name, type, opts = {})
61
59
  if type.is_a? Symbol or type.is_a? String
62
- type = "::Acfs::Attributes::#{type.to_s.classify}".constantize
60
+ type = "::Acfs::Model::Attributes::#{type.to_s.classify}".constantize
63
61
  end
64
62
 
65
- attrs.each do |attr|
66
- define_attribute attr.to_sym, type, opts
67
- end
63
+ define_attribute name.to_sym, type, opts
68
64
  end
69
65
 
70
66
  # Return list of possible attributes and default values for this model class.
71
67
  #
72
68
  # class User
73
- # attribute :name, String
74
- # attribute :age, Integer, default: 25
69
+ # include Acfs::Model
70
+ # attribute :name, :string
71
+ # attribute :age, :integer, default: 25
75
72
  # end
76
73
  # User.attributes # => { "name": nil, "age": 25 }
77
74
  #
@@ -79,10 +76,9 @@ module Acfs
79
76
  @attributes ||= {}
80
77
  end
81
78
 
82
-
83
79
  private
84
80
  def define_attribute(name, type, opts = {}) # :nodoc:
85
- @attributes ||= {}
81
+ @attributes ||= {}
86
82
  @attributes[name] = type.cast opts.has_key?(:default) ? opts[:default] : nil
87
83
 
88
84
  self.send :define_method, name do
@@ -101,5 +97,5 @@ end
101
97
  #
102
98
  Dir[File.dirname(__FILE__) + "/attributes/*.rb"].sort.each do |path|
103
99
  filename = File.basename(path)
104
- require "acfs/attributes/#{filename}"
100
+ require "acfs/model/attributes/#{filename}"
105
101
  end