acfs 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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