loquor 0.0.1
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 +7 -0
- data/.gitignore +18 -0
- data/CONTRIBUTING.md +30 -0
- data/Gemfile +4 -0
- data/LICENCE.md +617 -0
- data/README.md +88 -0
- data/Rakefile +14 -0
- data/lib/loquor.rb +37 -0
- data/lib/loquor/api_call.rb +11 -0
- data/lib/loquor/api_calls/index.rb +46 -0
- data/lib/loquor/api_calls/show.rb +13 -0
- data/lib/loquor/client.rb +19 -0
- data/lib/loquor/configuration.rb +36 -0
- data/lib/loquor/http_action.rb +16 -0
- data/lib/loquor/http_actions/get.rb +22 -0
- data/lib/loquor/http_actions/post.rb +33 -0
- data/lib/loquor/path_builder.rb +44 -0
- data/lib/loquor/representation.rb +22 -0
- data/lib/loquor/representations.rb +28 -0
- data/lib/loquor/version.rb +3 -0
- data/loquor.gemspec +29 -0
- data/test/api_calls/index_test.rb +72 -0
- data/test/client_test.rb +29 -0
- data/test/configuration_test.rb +58 -0
- data/test/http_actions/get_test.rb +49 -0
- data/test/http_actions/post_test.rb +59 -0
- data/test/path_builder_test.rb +46 -0
- data/test/representation_test.rb +30 -0
- data/test/representations_test.rb +16 -0
- data/test/test_helper.rb +25 -0
- metadata +181 -0
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Loquor
|
2
|
+
|
3
|
+
[](https://travis-ci.org/meducation/loquor)
|
4
|
+
[](https://gemnasium.com/meducation/loquor)
|
5
|
+
[](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/>.
|
data/Rakefile
ADDED
@@ -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
|
data/lib/loquor.rb
ADDED
@@ -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,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,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
|