tupplur 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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +115 -0
- data/Rakefile +8 -0
- data/lib/tupplur.rb +8 -0
- data/lib/tupplur/errors.rb +21 -0
- data/lib/tupplur/model_extensions.rb +142 -0
- data/lib/tupplur/rest_endpoint.rb +73 -0
- data/lib/tupplur/version.rb +3 -0
- data/test/rest_endpoint_test.rb +126 -0
- data/test/test_app/app.rb +20 -0
- data/test/test_app/config.ru +4 -0
- data/test/test_app/helpers.rb +4 -0
- data/test/test_app/models/secret.rb +7 -0
- data/test/test_app/models/user.rb +20 -0
- data/test/test_app/mongoid.yml +20 -0
- data/test/test_helper.rb +14 -0
- data/tupplur.gemspec +34 -0
- metadata +185 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f2b8e48210120a1b238121391fc8552891670c81
|
4
|
+
data.tar.gz: 494017fd71c91f6fd6c936200df729a7adb201cd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bf38291747032151dc4cf271d31b2fc560f123dc71b3b494006fb02d9a7fb3ff663b7d447a926ef4dddd9d9129dbdfc7f4f18b03c9d2d8735f11fdfd52dbd191
|
7
|
+
data.tar.gz: ed1ca103911a472313dfb7725c1dd54dc6d55165006a9ba3c71385b0b94051a05362d7eaacd471207ad37174d40fbe9ae1b86f0f81850ed66a0badb0b90b5a5f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Robin Edman
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# Tupplur
|
2
|
+
|
3
|
+
Tupplur extends your Mongoid 3.x models, allowing you to expose fields as readable
|
4
|
+
or writable from outside of your application. It includes a Rack REST adapter,
|
5
|
+
so that any Rack app can expose some or all of its' models as JSON easily
|
6
|
+
through a standard REST CRUD api.
|
7
|
+
|
8
|
+
This library is especially useful if you're building APIs for mobile or
|
9
|
+
Single Page Applications (maybe using Backbone, Spine, AngularJS or whichever
|
10
|
+
framework you prefer).
|
11
|
+
|
12
|
+
It's been designed to work well straight on top of Rack, or with lightweight
|
13
|
+
Rack-based frameworks such as Sinatra and Cuba. In fact, the REST adapter is
|
14
|
+
implemented as a Cuba application.
|
15
|
+
|
16
|
+
If you're using Rails you're probably better off with another solution at the
|
17
|
+
moment, since it might make more sense to use something that's more
|
18
|
+
integrated into the framework.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
gem 'tupplur'
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install tupplur
|
33
|
+
|
34
|
+
If necessary in your application:
|
35
|
+
```
|
36
|
+
require 'tupplur' # at the appropriate place.
|
37
|
+
```
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
Tupplur is divided into two parts: a model mixin and a Rack
|
42
|
+
application which acts like a REST endpoint.
|
43
|
+
|
44
|
+
### REST endpoint Rack application
|
45
|
+
After Mongoid has been set up, mount the Rack app on a route of your choice.
|
46
|
+
The details of how you do this will depend on which framework you're using.
|
47
|
+
Each model gets its' own endpoint. So repeat this for all
|
48
|
+
models for which you want to provide a REST interface.
|
49
|
+
|
50
|
+
In Cuba:
|
51
|
+
```ruby
|
52
|
+
on "/users" do
|
53
|
+
run Tupplur::RESTEndpoint.new(User)
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
In Rack (in config.ru):
|
58
|
+
```ruby
|
59
|
+
run Rack::URLMap.new("/" => YourApp.new,
|
60
|
+
"/users" => Tupplur::RESTEndpoint.new(User))
|
61
|
+
```
|
62
|
+
|
63
|
+
If you're using Sinatra you might prefer the Rack method to mounting it
|
64
|
+
within Sinatra itself.
|
65
|
+
|
66
|
+
### Model mixin
|
67
|
+
In your model:
|
68
|
+
```ruby
|
69
|
+
class User
|
70
|
+
include Mongoid::Document
|
71
|
+
include Tupplur::ModelExtensions
|
72
|
+
|
73
|
+
# Define which operations you want to support.
|
74
|
+
rest_interface :create,
|
75
|
+
:read,
|
76
|
+
:update,
|
77
|
+
:delete
|
78
|
+
|
79
|
+
# Fields that are to be both readable and writable.
|
80
|
+
externally_accessible :name,
|
81
|
+
:email
|
82
|
+
|
83
|
+
# Read-only fields.
|
84
|
+
externally_readable :active
|
85
|
+
|
86
|
+
# Put your regular Mongoid model code here (or anywhere you want to as the ordering doesn't matter.)
|
87
|
+
field :name, type: String
|
88
|
+
field :email, type: String
|
89
|
+
field :password, type: String
|
90
|
+
field :active, type: Boolean, default: false
|
91
|
+
end
|
92
|
+
```
|
93
|
+
This is where you define what parts of your model you want to
|
94
|
+
expose outside of your backend application. This is configured
|
95
|
+
in a similar manner to the way we choose to expose attributes on
|
96
|
+
objects in plain Ruby. There we use atr_reader and
|
97
|
+
attr_accessor. Tupplur includes the corresponding methods
|
98
|
+
externally_readable and externally_accessible.
|
99
|
+
|
100
|
+
You also define what REST operations a given model should
|
101
|
+
support using the rest_interface method. Just like externally_readable and
|
102
|
+
externally_writable it takes one or more symbols as arguments: :create, :read,
|
103
|
+
:update, :delete. The default is to not support any operation. You may
|
104
|
+
see this as whitelisting operations.
|
105
|
+
|
106
|
+
### Examples
|
107
|
+
In the test directory you'll find an example app which uses the Cuba framework.
|
108
|
+
|
109
|
+
## Contributing
|
110
|
+
|
111
|
+
1. Fork it
|
112
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
113
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
114
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
115
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/tupplur.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Tupplur
|
4
|
+
module Error
|
5
|
+
class RESTOperationNotAllowed < StandardError
|
6
|
+
def message
|
7
|
+
"The #{@operation} REST operation is not allowed."
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(operation = 'unspecified')
|
11
|
+
@operation = operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.log_exception(e)
|
16
|
+
puts "Exception: #{e}"
|
17
|
+
puts "Message: #{e.message}"
|
18
|
+
puts e.backtrace.join("\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "active_support/all"
|
4
|
+
|
5
|
+
module Tupplur
|
6
|
+
module ModelExtensions
|
7
|
+
def self.included(base)
|
8
|
+
base.const_set(:EXTERNALLY_READABLE_FIELDS, ['_id'])
|
9
|
+
base.const_set(:EXTERNALLY_ACCESSIBLE_FIELDS, [])
|
10
|
+
base.const_set(:REST_INTERFACE, [])
|
11
|
+
|
12
|
+
base.send(:include, InstanceMethods)
|
13
|
+
base.send(:extend, ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def as_external_document
|
18
|
+
doc = self.as_document
|
19
|
+
|
20
|
+
# note: id fix for client side libraries like Spine.js,
|
21
|
+
# who rely on an id attribute being present.
|
22
|
+
doc['id'] = doc['_id']
|
23
|
+
|
24
|
+
doc.slice(*readable_fields + ['id'])
|
25
|
+
end
|
26
|
+
|
27
|
+
def external_update!(document_as_hash)
|
28
|
+
allowed_fields = filter_accessible_fields(document_as_hash)
|
29
|
+
update_attributes!(allowed_fields)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Does the model allow a certain REST operation?
|
33
|
+
# If no operation given: Does the model allow any REST operation at all?
|
34
|
+
def rest?(operation)
|
35
|
+
if operation
|
36
|
+
self.class.const_get(:REST_INTERFACE).include?(operation)
|
37
|
+
else
|
38
|
+
! self.class.const_get(:REST_INTERFACE).empty?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def filter_accessible_fields(hsh)
|
45
|
+
hsh.slice(*self.class.const_get(:EXTERNALLY_ACCESSIBLE_FIELDS).map(&:to_s))
|
46
|
+
end
|
47
|
+
|
48
|
+
def readable_fields
|
49
|
+
(self.class.const_get(:EXTERNALLY_ACCESSIBLE_FIELDS) + self.class.const_get(:EXTERNALLY_READABLE_FIELDS)).map(&:to_s)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
# Does the model allow a certain REST operation?
|
55
|
+
# If no operation given: Does the model allow any REST operation at all?
|
56
|
+
def rest?(operation = nil)
|
57
|
+
if operation
|
58
|
+
const_get(:REST_INTERFACE).include?(operation)
|
59
|
+
else
|
60
|
+
! const_get(:REST_INTERFACE).empty?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def rest_read(document_id = nil)
|
65
|
+
if document_id
|
66
|
+
rest_read_document(document_id)
|
67
|
+
else
|
68
|
+
rest_read_all
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def rest_update(document_id, client_model)
|
73
|
+
raise Tupplur::Error::RESTOperationNotAllowed.new('update') unless rest?(:update)
|
74
|
+
|
75
|
+
find(document_id).external_update!(client_model)
|
76
|
+
end
|
77
|
+
|
78
|
+
def rest_delete(document_id)
|
79
|
+
raise Tupplur::Error::RESTOperationNotAllowed.new('delete') unless rest?(:delete)
|
80
|
+
|
81
|
+
find(document_id).delete
|
82
|
+
end
|
83
|
+
|
84
|
+
def rest_create(client_fields)
|
85
|
+
raise Tupplur::Error::RESTOperationNotAllowed.new('create') unless rest?(:create)
|
86
|
+
|
87
|
+
external_create!(client_fields)
|
88
|
+
end
|
89
|
+
|
90
|
+
def rest_read_document(document_id)
|
91
|
+
raise Tupplur::Error::RESTOperationNotAllowed.new('read') unless rest?(:read)
|
92
|
+
|
93
|
+
find(document_id).as_external_document
|
94
|
+
end
|
95
|
+
|
96
|
+
def rest_read_all
|
97
|
+
raise Tupplur::Error::RESTOperationNotAllowed.new('read') unless rest?(:read)
|
98
|
+
|
99
|
+
all.map { |m| m.as_external_document }
|
100
|
+
end
|
101
|
+
|
102
|
+
def external_create!(fields)
|
103
|
+
allowed_fields = filter_accessible_fields(fields)
|
104
|
+
create!(allowed_fields)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Externally accessible fields and embedded documents.
|
110
|
+
def externally_accessible(*fields)
|
111
|
+
const_get(:EXTERNALLY_ACCESSIBLE_FIELDS).push(*fields)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Externally readable fields and embedded documents.
|
115
|
+
def externally_readable(*fields)
|
116
|
+
const_get(:EXTERNALLY_READABLE_FIELDS).push(*fields)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Used to define allowed REST operations (e.g. :read, :create, :update, :delete).
|
120
|
+
# Example usage:
|
121
|
+
# class MyModel
|
122
|
+
# include Mongoid::Document
|
123
|
+
# include Tupplur::ModelExtensions
|
124
|
+
#
|
125
|
+
# rest_interface :read, :update, :delete
|
126
|
+
#
|
127
|
+
def rest_interface(*operations)
|
128
|
+
const_get(:REST_INTERFACE).push(*operations)
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def filter_accessible_fields(hsh)
|
133
|
+
hsh.slice(*const_get(:EXTERNALLY_ACCESSIBLE_FIELDS).map(&:to_s))
|
134
|
+
end
|
135
|
+
|
136
|
+
def readable_fields
|
137
|
+
(const_get(:EXTERNALLY_ACCESSIBLE_FIELDS) + const_get(:EXTERNALLY_READABLE_FIELDS)).map(&:to_s)
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'cuba'
|
4
|
+
|
5
|
+
module Tupplur
|
6
|
+
class RESTEndpoint < Cuba
|
7
|
+
attr_reader :model
|
8
|
+
|
9
|
+
def initialize(model)
|
10
|
+
@model = model
|
11
|
+
super() do
|
12
|
+
on ":id" do |document_id|
|
13
|
+
# REST read document
|
14
|
+
on get do
|
15
|
+
begin
|
16
|
+
send_json(@model.rest_read(document_id))
|
17
|
+
rescue Tupplur::Error::RESTOperationNotAllowed => e
|
18
|
+
res.status = 401
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# REST update document
|
23
|
+
on put, param('data') do |client_model|
|
24
|
+
begin
|
25
|
+
send_json(@model.rest_update(document_id, client_model))
|
26
|
+
rescue Tupplur::Error::RESTOperationNotAllowed => e
|
27
|
+
res.status = 401
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# REST delete document
|
32
|
+
on delete do
|
33
|
+
begin
|
34
|
+
send_json(@model.rest_delete(document_id))
|
35
|
+
rescue Tupplur::Error::RESTOperationNotAllowed => e
|
36
|
+
res.status = 401
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# REST read whole collection
|
42
|
+
on get do
|
43
|
+
begin
|
44
|
+
send_json(@model.rest_read)
|
45
|
+
rescue Tupplur::Error::RESTOperationNotAllowed => e
|
46
|
+
res.status = 401
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# REST create document
|
51
|
+
on post, param('data') do |client_fields|
|
52
|
+
begin
|
53
|
+
@model.rest_create(client_fields)
|
54
|
+
rescue Tupplur::Error::RESTOperationNotAllowed => e
|
55
|
+
res.status = 401
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.define
|
62
|
+
raise NotImplementedError, "Use .new and run the app instead."
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def send_json(document)
|
68
|
+
res['Content-Type'] = 'application/json; charset=utf-8'
|
69
|
+
res.write(document.to_json)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
require_relative "../lib/tupplur"
|
3
|
+
|
4
|
+
module RESTEndPointTest
|
5
|
+
class Base < TupplurTestCase
|
6
|
+
def tabula_rasa
|
7
|
+
User.delete_all
|
8
|
+
Secret.delete_all
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup
|
12
|
+
tabula_rasa
|
13
|
+
|
14
|
+
User.create!(name: "Adam",
|
15
|
+
email: "adam@example.com",
|
16
|
+
password: "verysecurepassword")
|
17
|
+
User.create!(name: "Brenda",
|
18
|
+
email: "brenda@example.com",
|
19
|
+
password: "verysecurepassword")
|
20
|
+
|
21
|
+
Secret.create!(message: "Super secret info.")
|
22
|
+
end
|
23
|
+
|
24
|
+
def teardown
|
25
|
+
tabula_rasa
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class GetTest < Base
|
30
|
+
test "get all documents returns only readable and accessible attributes" do
|
31
|
+
get "/users"
|
32
|
+
|
33
|
+
expected_response = [{name: "Adam", email: "adam@example.com", active: false},
|
34
|
+
{name: "Brenda", email: "brenda@example.com", active: false}]
|
35
|
+
response = ActiveSupport::JSON.decode(last_response.body).map(&:symbolize_keys!)
|
36
|
+
|
37
|
+
assert_equal(expected_response, response.map { |m| m.except(:_id, :id) })
|
38
|
+
end
|
39
|
+
|
40
|
+
test "get document with id returns only readable and accessible attributes" do
|
41
|
+
brenda_id = User.find_by(name: "Brenda")._id
|
42
|
+
get "/users/#{brenda_id}"
|
43
|
+
|
44
|
+
expected_response = {name: "Brenda", email: "brenda@example.com", active: false}
|
45
|
+
response = ActiveSupport::JSON.decode(last_response.body).symbolize_keys!
|
46
|
+
|
47
|
+
assert_equal(expected_response, response.except(:_id, :id))
|
48
|
+
end
|
49
|
+
|
50
|
+
test "cannot get resource that does not allow it" do
|
51
|
+
get "/secrets"
|
52
|
+
|
53
|
+
assert_equal(401, last_response.status)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class UpdateTest < Base
|
58
|
+
test "update accessible attribute" do
|
59
|
+
user = User.find_by(name: "Brenda")
|
60
|
+
put "/users/#{user._id}", {data: {name: "Brian"}}
|
61
|
+
user.reload
|
62
|
+
|
63
|
+
assert_equal("Brian", user.name)
|
64
|
+
end
|
65
|
+
|
66
|
+
test "cannot update readonly attribute" do
|
67
|
+
user = User.last
|
68
|
+
put "/users/#{user._id}", {data: {active: true}}
|
69
|
+
user.reload
|
70
|
+
|
71
|
+
refute(user.active)
|
72
|
+
end
|
73
|
+
|
74
|
+
test "cannot update internal attribute" do
|
75
|
+
user = User.last
|
76
|
+
put "users/#{user._id}", {data: {password: "anotherpassword"}}
|
77
|
+
user.reload
|
78
|
+
|
79
|
+
assert_equal("verysecurepassword", user.password)
|
80
|
+
end
|
81
|
+
|
82
|
+
test "cannot update resource that does not allow it" do
|
83
|
+
secret = Secret.first
|
84
|
+
put "secrets/#{secret._id}", {data: {message: "I say"}}
|
85
|
+
secret.reload
|
86
|
+
|
87
|
+
assert_equal("Super secret info.", secret.message)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class DeleteTest < Base
|
92
|
+
test "delete resource" do
|
93
|
+
user_id = User.first._id
|
94
|
+
delete "/users/#{user_id}"
|
95
|
+
|
96
|
+
refute(User.find(user_id))
|
97
|
+
end
|
98
|
+
|
99
|
+
test "cannot delete resource that does not allow it" do
|
100
|
+
secret_id = Secret.first._id
|
101
|
+
delete "/secrets/#{secret_id}"
|
102
|
+
|
103
|
+
assert(Secret.find(secret_id))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class CreateTest < Base
|
108
|
+
test "create resource" do
|
109
|
+
post "/users", {data: {name: "Charles", email: "charles@example.com"}}
|
110
|
+
|
111
|
+
assert(User.find_by(name: "Charles"))
|
112
|
+
end
|
113
|
+
|
114
|
+
test "cannot set readable attributes" do
|
115
|
+
post "/users", {data: {name: "Roger", active: true}}
|
116
|
+
|
117
|
+
refute(User.find_by(name: "Roger").active)
|
118
|
+
end
|
119
|
+
|
120
|
+
test "cannot create resource that does not allow it" do
|
121
|
+
post "/secrets", {data: {message: "Hey"}}
|
122
|
+
|
123
|
+
refute(Secret.find_by(message: "Hey"))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "mongoid"
|
2
|
+
require_relative "helpers"
|
3
|
+
|
4
|
+
cd_to_here
|
5
|
+
Mongoid.load!("./mongoid.yml")
|
6
|
+
|
7
|
+
require_relative "../../lib/tupplur"
|
8
|
+
require_relative "models/user"
|
9
|
+
require_relative "models/secret"
|
10
|
+
|
11
|
+
Cuba.define do
|
12
|
+
on "users" do
|
13
|
+
run Tupplur::RESTEndpoint.new(User)
|
14
|
+
end
|
15
|
+
|
16
|
+
on "secrets" do
|
17
|
+
run Tupplur::RESTEndpoint.new(Secret)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class User
|
2
|
+
include Mongoid::Document
|
3
|
+
include Tupplur::ModelExtensions
|
4
|
+
|
5
|
+
rest_interface :create,
|
6
|
+
:read,
|
7
|
+
:update,
|
8
|
+
:delete
|
9
|
+
|
10
|
+
externally_accessible :name,
|
11
|
+
:email
|
12
|
+
|
13
|
+
externally_readable :active
|
14
|
+
|
15
|
+
field :name, type: String
|
16
|
+
field :email, type: String
|
17
|
+
field :password, type: String
|
18
|
+
field :active, type: Boolean, default: false
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Tell Mongoid which environment this configuration is for.
|
2
|
+
development:
|
3
|
+
# This starts the session configuration settings. You may have as
|
4
|
+
# many sessions as you like, but you must have at least 1 named
|
5
|
+
# 'default'.
|
6
|
+
sessions:
|
7
|
+
# Define the default session.
|
8
|
+
default:
|
9
|
+
# A session can have any number of hosts. Usually 1 for a single
|
10
|
+
# server setup, and at least 3 for a replica set. Hosts must be
|
11
|
+
# an array of host:port pairs. This session is single server.
|
12
|
+
hosts:
|
13
|
+
- 127.0.0.1:27017
|
14
|
+
# Define the default database name.
|
15
|
+
database: tupplurtest
|
16
|
+
# Set username and password if you need to.
|
17
|
+
# username: user
|
18
|
+
# password: password
|
19
|
+
options:
|
20
|
+
raise_not_found_error: false
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "turn/autorun"
|
2
|
+
require "active_support/all"
|
3
|
+
require "rack/test"
|
4
|
+
require_relative "test_app/app"
|
5
|
+
|
6
|
+
Turn.config.natural = true
|
7
|
+
|
8
|
+
class TupplurTestCase < ActiveSupport::TestCase
|
9
|
+
include Rack::Test::Methods
|
10
|
+
|
11
|
+
def app
|
12
|
+
Cuba
|
13
|
+
end
|
14
|
+
end
|
data/tupplur.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#lib = File.expand_path('../lib', __FILE__)
|
3
|
+
#$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
# require 'tupplur/version'
|
5
|
+
require File.expand_path('../lib/tupplur/version', __FILE__)
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "tupplur"
|
9
|
+
spec.version = Tupplur::VERSION
|
10
|
+
spec.authors = ["Robin Edman"]
|
11
|
+
spec.email = ["robin.edman@gmail.com"]
|
12
|
+
spec.description = %q{Tupplur extends Mongoid, allowing fields to be exposed as readable or writable from outside of your application.}
|
13
|
+
spec.summary = spec.description
|
14
|
+
spec.homepage = ""
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
spec.add_development_dependency "turn", "~> 0.9"
|
25
|
+
spec.add_development_dependency "activesupport", "~> 3.0"
|
26
|
+
spec.add_development_dependency "rack-test", "~> 0.6.2"
|
27
|
+
spec.add_development_dependency "virtus", "~> 1.0"
|
28
|
+
spec.add_development_dependency "mongoid", "~> 3.0"
|
29
|
+
|
30
|
+
spec.add_runtime_dependency "cuba", "~>3.1.0"
|
31
|
+
# Mongoid is an implicitly assumed dependency. So is ActiveSupport.
|
32
|
+
# We don't specify those versions since we don't want to risk conflicts with
|
33
|
+
# Mongoid.
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tupplur
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robin Edman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: turn
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.9'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.6.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.6.2
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: virtus
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: mongoid
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: cuba
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 3.1.0
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 3.1.0
|
125
|
+
description: Tupplur extends Mongoid, allowing fields to be exposed as readable or
|
126
|
+
writable from outside of your application.
|
127
|
+
email:
|
128
|
+
- robin.edman@gmail.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- .gitignore
|
134
|
+
- Gemfile
|
135
|
+
- LICENSE.txt
|
136
|
+
- README.md
|
137
|
+
- Rakefile
|
138
|
+
- lib/tupplur.rb
|
139
|
+
- lib/tupplur/errors.rb
|
140
|
+
- lib/tupplur/model_extensions.rb
|
141
|
+
- lib/tupplur/rest_endpoint.rb
|
142
|
+
- lib/tupplur/version.rb
|
143
|
+
- test/rest_endpoint_test.rb
|
144
|
+
- test/test_app/app.rb
|
145
|
+
- test/test_app/config.ru
|
146
|
+
- test/test_app/helpers.rb
|
147
|
+
- test/test_app/models/secret.rb
|
148
|
+
- test/test_app/models/user.rb
|
149
|
+
- test/test_app/mongoid.yml
|
150
|
+
- test/test_helper.rb
|
151
|
+
- tupplur.gemspec
|
152
|
+
homepage: ''
|
153
|
+
licenses:
|
154
|
+
- MIT
|
155
|
+
metadata: {}
|
156
|
+
post_install_message:
|
157
|
+
rdoc_options: []
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - '>='
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - '>='
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
requirements: []
|
171
|
+
rubyforge_project:
|
172
|
+
rubygems_version: 2.0.3
|
173
|
+
signing_key:
|
174
|
+
specification_version: 4
|
175
|
+
summary: Tupplur extends Mongoid, allowing fields to be exposed as readable or writable
|
176
|
+
from outside of your application.
|
177
|
+
test_files:
|
178
|
+
- test/rest_endpoint_test.rb
|
179
|
+
- test/test_app/app.rb
|
180
|
+
- test/test_app/config.ru
|
181
|
+
- test/test_app/helpers.rb
|
182
|
+
- test/test_app/models/secret.rb
|
183
|
+
- test/test_app/models/user.rb
|
184
|
+
- test/test_app/mongoid.yml
|
185
|
+
- test/test_helper.rb
|