loquor 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ # Loquor
2
+
3
+ [![Build Status](https://travis-ci.org/meducation/loquor.png)](https://travis-ci.org/meducation/loquor)
4
+ [![Dependencies](https://gemnasium.com/meducation/loquor.png?travis)](https://gemnasium.com/meducation/loquor)
5
+ [![Code Climate](https://codeclimate.com/github/meducation/loquor.png)](https://codeclimate.com/github/meducation/loquor)
6
+
7
+ Handles calls to the Meducation API via an ActiveRecord-style interface
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'loquor'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install loquor
22
+
23
+
24
+ ## Usage
25
+
26
+ You will want to set up some configuration variables.
27
+ ``` ruby
28
+ Loquor.config do |config|
29
+ config.access_id = "Username"
30
+ config.secret_key = "SecretKey1929292"
31
+ config.endpoint = "http://www.meducation.net"
32
+ end
33
+ ```
34
+
35
+ Now you make requests to get, create, update, destroy and list a range of objects, like this:
36
+
37
+ ```ruby
38
+ User.where(email: "jeremy@meducation.net").where(name: "Jeremy").each do |user|
39
+ p "The user with id ##{user['id']} is #{user['name']}."
40
+ end
41
+
42
+ User.find(2) # => {id: 2, name: "Jeremy Walker"}
43
+
44
+ User.create(name: "Jeremy Walker", email: "jeremy@meducation.net") # => {id: 2, name: "Jeremy Walker", email "jeremy@meducation.net"}
45
+ ```
46
+
47
+ ### Supported Objects
48
+
49
+ The following are currently endpoints are supported:
50
+ * Group Discussions
51
+ * Group Discussion Posts
52
+ * Media Files
53
+ * Users
54
+
55
+ ### Is it any good?
56
+
57
+ [Yes.](http://news.ycombinator.com/item?id=3067434)
58
+
59
+ ## Contributing
60
+
61
+ Firstly, thank you!! :heart::sparkling_heart::heart:
62
+
63
+ We'd love to have you involved. Please read our [contributing guide](https://github.com/meducation/loquor/tree/master/CONTRIBUTING.md) for information on how to get stuck in.
64
+
65
+ ### Contributors
66
+
67
+ This project is managed by the [Meducation team](http://company.meducation.net/about#team).
68
+
69
+ These individuals have come up with the ideas and written the code that made this possible:
70
+
71
+ - [Jeremy Walker](http://github.com/iHID)
72
+
73
+ ## Licence
74
+
75
+ Copyright (C) 2013 New Media Education Ltd
76
+
77
+ This program is free software: you can redistribute it and/or modify
78
+ it under the terms of the GNU Affero General Public License as published by
79
+ the Free Software Foundation, either version 3 of the License, or
80
+ (at your option) any later version.
81
+
82
+ This program is distributed in the hope that it will be useful,
83
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
84
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
85
+ GNU Affero General Public License for more details.
86
+
87
+ A copy of the GNU Affero General Public License is available in [Licence.md](https://github.com/meducation/loquor/blob/master/LICENCE.md)
88
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "test/**/*_test.rb"
6
+ end
7
+
8
+ namespace :test do
9
+ Rake::TestTask.new(:local) do |t|
10
+ t.pattern = "test/{components/,services/,helpers/,}*_test.rb"
11
+ end
12
+ end
13
+
14
+ task default: :test
@@ -0,0 +1,37 @@
1
+ require 'rest-client'
2
+ require 'api-auth'
3
+ require 'filum'
4
+
5
+ require "loquor/version"
6
+ require "loquor/configuration"
7
+ require "loquor/client"
8
+ require 'loquor/path_builder'
9
+ require 'loquor/representation'
10
+ require 'loquor/representations'
11
+
12
+ require 'loquor/api_call'
13
+ require "loquor/http_action"
14
+
15
+ module Loquor
16
+ def self.config
17
+ if block_given?
18
+ yield loquor.config
19
+ else
20
+ loquor.config
21
+ end
22
+ end
23
+
24
+ def self.get(url)
25
+ loquor.get(url)
26
+ end
27
+
28
+ def self.post(url, payload)
29
+ loquor.post(url, payload)
30
+ end
31
+
32
+ private
33
+
34
+ def self.loquor
35
+ @loquor ||= Client.new
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ module Loquor
2
+ class ApiCall
3
+ include Loquor::PathBuilder
4
+ def initialize(path)
5
+ setup_path_builder(path)
6
+ end
7
+ end
8
+ end
9
+
10
+ require 'loquor/api_calls/show'
11
+ require 'loquor/api_calls/index'
@@ -0,0 +1,46 @@
1
+ module Loquor
2
+ class ApiCall::Index < ApiCall
3
+
4
+ attr_reader :criteria
5
+
6
+ def initialize(path)
7
+ super(path)
8
+ @criteria = {}
9
+ end
10
+
11
+ def where(value)
12
+ value.each do |key, value|
13
+ @criteria[key] = value
14
+ end
15
+ self
16
+ end
17
+
18
+ # Proxy everything to the results so that this this class
19
+ # transparently acts as an Array.
20
+ def method_missing(name, *args, &block)
21
+ results.send(name, *args, &block)
22
+ end
23
+
24
+ private
25
+
26
+ def results
27
+ if @results.nil?
28
+ @results = Loquor.get(generate_url)
29
+ end
30
+ @results
31
+ end
32
+
33
+ def generate_url
34
+ query_string = @criteria.map { |key,value|
35
+ if value.is_a?(String)
36
+ "#{key}=#{URI.encode(value)}"
37
+ elsif value.is_a?(Array)
38
+ "#{key}=[#{URI.encode(value.join(","))}]"
39
+ else
40
+ raise LoquorError.new("Filter values must be strings or arrays.")
41
+ end
42
+ }.join("&")
43
+ "#{build_path}?#{query_string}"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ module Loquor
2
+ class ApiCall::Show < ApiCall
3
+
4
+ def initialize(path, id)
5
+ super(path)
6
+ @id = id
7
+ end
8
+
9
+ def execute
10
+ Loquor.get("#{build_path}/#{@id}")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Loquor
2
+ class Client
3
+ attr_reader :config
4
+
5
+ def initialize
6
+ @config = Configuration.new
7
+ end
8
+
9
+ def get(url)
10
+ deps = {config: @config}
11
+ HttpAction::Get.get(url, deps)
12
+ end
13
+
14
+ def post(url, payload)
15
+ deps = {config: @config}
16
+ HttpAction::Post.post(url, payload, deps)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ module Loquor
2
+ class LoquorError < StandardError
3
+ end
4
+ class LoquorConfigurationError < LoquorError
5
+ end
6
+
7
+ class Configuration
8
+
9
+ SETTINGS = [
10
+ :logger, :access_id, :secret_key, :endpoint
11
+ ]
12
+
13
+ attr_writer *SETTINGS
14
+
15
+ def initialize
16
+ Filum.config do |config|
17
+ config.logfile = "./log/loquor.log"
18
+ end
19
+ logger = Filum.logger
20
+ end
21
+
22
+ SETTINGS.each do |setting|
23
+ define_method setting do
24
+ get_or_raise(setting)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def get_or_raise(setting)
31
+ instance_variable_get("@#{setting.to_s}") ||
32
+ raise(LoquorConfigurationError.new("Configuration for #{setting} is not set"))
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,16 @@
1
+ module Loquor
2
+ class HttpAction
3
+ def initialize(url, deps)
4
+ @url = url
5
+ @config = deps[:config]
6
+ end
7
+
8
+ def signed_request
9
+ ApiAuth.sign!(request, @config.access_id, @config.secret_key)
10
+ end
11
+ end
12
+ end
13
+
14
+ require 'loquor/http_actions/get'
15
+ require 'loquor/http_actions/post'
16
+
@@ -0,0 +1,22 @@
1
+ module Loquor
2
+ class HttpAction::Get < HttpAction
3
+ def self.get(url, deps)
4
+ new(url, deps).get
5
+ end
6
+
7
+ def initialize(url, deps)
8
+ super
9
+ end
10
+
11
+ def get
12
+ JSON.parse(signed_request.execute)
13
+ end
14
+
15
+ private
16
+ def request
17
+ full_url = "#{@config.endpoint}#{@url}"
18
+ RestClient::Request.new(url: full_url, method: :get)
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,33 @@
1
+ module Loquor
2
+ class HttpAction::Post < HttpAction
3
+ def self.post(url, payload, deps)
4
+ new(url, payload, deps).post
5
+ end
6
+
7
+ def initialize(url, payload, deps)
8
+ super(url, deps)
9
+ @payload = payload
10
+ end
11
+
12
+ def post
13
+ JSON.parse(signed_request.execute)
14
+ end
15
+
16
+ private
17
+
18
+ def signed_request
19
+ signed_request = super
20
+ p signed_request # If you take this line out - it all breaks. Yeah...
21
+ signed_request
22
+ end
23
+
24
+ def request
25
+ full_url = "#{@config.endpoint}#{@url}"
26
+ RestClient::Request.new(url: full_url,
27
+ accept: :json,
28
+ payload: @payload.to_json,
29
+ headers: {'Content-type' => 'application/json'},
30
+ method: :post)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ module Loquor
2
+
3
+ class MissingUrlComponentError < LoquorError
4
+ def initialize(url_component)
5
+ @url_component = url_component
6
+ end
7
+
8
+ def message
9
+ "#{url_component} has not been set. Use Object.for_#{url_component}"
10
+ end
11
+ end
12
+
13
+ module PathBuilder
14
+ PATH_PART_REGEX = /:[a-z0-9_]+/
15
+
16
+ def setup_path_builder(path)
17
+ path.split('/').each do |path_part|
18
+ next unless path_part =~ PATH_PART_REGEX
19
+ path_part = path_part[1..-1]
20
+ method_name = "for_#{path_part}"
21
+
22
+ self.class.send :define_method, method_name do |id|
23
+ @path_parts ||= {}
24
+ @path_parts[path_part.to_sym] = id
25
+ self
26
+ end
27
+
28
+ self.class.class_eval <<-EOS
29
+ def self.#{method_name}(*args)
30
+ new.#{method_name}(*args)
31
+ end
32
+ EOS
33
+ end
34
+
35
+ self.class.send :define_method, :build_path do
36
+ path.gsub(PATH_PART_REGEX) do |path_part|
37
+ path_part = path_part[1..-1].to_sym
38
+ @path_parts ||= {}
39
+ @path_parts.fetch(path_part) { raise MissingUrlComponentError.new(path_part) }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ module Loquor
2
+ module Representation
3
+ module ClassMethods
4
+
5
+ [:find, :where].each do |proxy|
6
+ define_method proxy do |*args|
7
+ new.send proxy, *args
8
+ end
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+ def find(id)
14
+ ApiCall::Show.new(self.class.path, id).execute
15
+ end
16
+
17
+ def where(*args)
18
+ ApiCall::Index.new(self.class.path).where(*args)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ {
2
+ "Group::Discussion" => "/group/:group_id/discussions",
3
+ "Group::DiscussionPost" => "/group/:group_id/discussion",
4
+ "MediaFile" => "/media_files",
5
+ "User" => "/users"
6
+ }.each do |name, path|
7
+ klass = Class.new(Object) do
8
+ extend Loquor::Representation::ClassMethods
9
+ include Loquor::Representation::InstanceMethods
10
+
11
+ define_method :path do
12
+ path
13
+ end
14
+ end
15
+
16
+ # Split off the Group and Discussion parts
17
+ name_parts = name.split("::")
18
+ klass_name = name_parts.pop
19
+
20
+ # Create base modules
21
+ const = Loquor
22
+ name_parts.each do |name_part|
23
+ const.const_set name_part, Module unless const.const_defined?(name_part)
24
+ end
25
+
26
+ # Define the actual klass at the right point
27
+ const.const_set klass_name, klass
28
+ end