ohm-scope 0.1.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.
- checksums.yaml +7 -0
- data/.gems +2 -0
- data/.gitignore +2 -0
- data/LICENSE +20 -0
- data/Makefile +10 -0
- data/README.md +81 -0
- data/lib/ohm/scope.rb +55 -0
- data/ohm-scope.gemspec +14 -0
- data/test/all.rb +141 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 47bf355b27124d30d8e908b38b136a6ca3bdb20c
|
4
|
+
data.tar.gz: 690faf13bc68a01e92797baeb1ef471404eb80a6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c664eccb72e07bb3216747ed10fdacc27123507f14a0af01a08e4500e65833a046722e0a07eca5b2a5220ecfa44adba52925469b368c00ba2badb0de51188368
|
7
|
+
data.tar.gz: a5bf96adecca02db629dddb969b1f5a4cac3edb3e20d29596e81665dbe7850477068aacc41e999e0c84cae1fb3d75c091113a82cf9b697347487494ad3ebc34c
|
data/.gems
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2017 Steven Weiss
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# Ohm::Scope
|
2
|
+
|
3
|
+
Ohm::Scope wraps Ohm::Model to let you work with user
|
4
|
+
input in a way that is both safe and familiar to Ohm users.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
`$ gem install ohm-scope`
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
Ohm::Scope implements all of Ohm::Model's public class methods while maintaining
|
13
|
+
the same contract as using Ohm::Model directly. If you're comfortable with Ohm::Model,
|
14
|
+
you should feel at home using Ohm::Scope. The one method new to Ohm::Scope
|
15
|
+
is the `#build` method. It is a factory method that delegates
|
16
|
+
to the scope's model class `::new` method by default, but also lets us
|
17
|
+
pass an argument for a different class if we want to construct something other
|
18
|
+
than a model.
|
19
|
+
|
20
|
+
### Example
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'syro'
|
24
|
+
require 'ohm'
|
25
|
+
require 'ohm/scope'
|
26
|
+
|
27
|
+
class User < Ohm::Model
|
28
|
+
collection :posts, :Post
|
29
|
+
end
|
30
|
+
|
31
|
+
class Post < Ohm::Model
|
32
|
+
attribute :title
|
33
|
+
|
34
|
+
unique :title
|
35
|
+
|
36
|
+
reference :user, :User
|
37
|
+
end
|
38
|
+
|
39
|
+
class Deck < Syro::Deck
|
40
|
+
# A convenience helper for initializing scopes.
|
41
|
+
def scope(model, user)
|
42
|
+
Ohm::Scope.new(model, { user_id: user.id })
|
43
|
+
end
|
44
|
+
|
45
|
+
# A pretend authentication solution.
|
46
|
+
def curent_user
|
47
|
+
User.with(:auth_token, req.session[:auth_token])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
App = Syro.new(Deck) do
|
52
|
+
on 'posts' do
|
53
|
+
# Create a new scope that will allow the `current_user` to
|
54
|
+
# have access to only their posts.
|
55
|
+
@posts = scope(Post, current_user)
|
56
|
+
|
57
|
+
post do
|
58
|
+
# With our `@posts` scope, we can safely create new records via request params.
|
59
|
+
# The params should probably still be validated for correctness,
|
60
|
+
# but the scope protects us from the params containing a `:user_id` field
|
61
|
+
# with an id other than that of the authenticated user.
|
62
|
+
res.json @posts.create(params)
|
63
|
+
end
|
64
|
+
|
65
|
+
on :id do
|
66
|
+
# Finders are also scoped. If the url param `:id` contains
|
67
|
+
# an id for a post that doesn't belong to the authenticated user,
|
68
|
+
# our scope will return nil as if it doesn't exist.
|
69
|
+
@post = @posts[inbox[:id]]
|
70
|
+
|
71
|
+
patch do
|
72
|
+
...
|
73
|
+
end
|
74
|
+
|
75
|
+
delete do
|
76
|
+
...
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
ruby```
|
data/lib/ohm/scope.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Ohm
|
2
|
+
class Scope
|
3
|
+
VERSION = '0.1.0'.freeze
|
4
|
+
|
5
|
+
def initialize(model, dict)
|
6
|
+
@model, @dict = model, dict
|
7
|
+
end
|
8
|
+
|
9
|
+
def create(**attributes)
|
10
|
+
build(**attributes).save
|
11
|
+
end
|
12
|
+
|
13
|
+
def build(klass = model, **attributes)
|
14
|
+
klass.new(scoped(attributes))
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](id)
|
18
|
+
all[id]
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch(ids)
|
22
|
+
all.fetch(ids.select { |id| exists?(id) })
|
23
|
+
end
|
24
|
+
|
25
|
+
def with(key, val)
|
26
|
+
instance = model.with(key, val)
|
27
|
+
|
28
|
+
if instance && exists?(instance.id)
|
29
|
+
instance
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def exists?(id)
|
36
|
+
!!all[id]
|
37
|
+
end
|
38
|
+
|
39
|
+
def all
|
40
|
+
find({})
|
41
|
+
end
|
42
|
+
|
43
|
+
def find(attributes)
|
44
|
+
model.find(scoped(attributes))
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
attr_reader :model, :dict
|
50
|
+
|
51
|
+
def scoped(attributes = {})
|
52
|
+
attributes.merge(dict)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/ohm-scope.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require './lib/ohm/scope'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'ohm-scope'
|
5
|
+
s.summary = 'Ohm::Scope'
|
6
|
+
s.version = Ohm::Scope::VERSION
|
7
|
+
s.authors = ['Steve Weiss']
|
8
|
+
s.email = ['weissst@mail.gvsu.edu']
|
9
|
+
s.homepage = 'https://github.com/sirscriptalot/ohm-scope'
|
10
|
+
s.license = 'MIT'
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
|
13
|
+
s.add_dependency 'ohm', '~> 3.0'
|
14
|
+
end
|
data/test/all.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'ohm'
|
3
|
+
require_relative '../lib/ohm/scope'
|
4
|
+
|
5
|
+
class User < Ohm::Model
|
6
|
+
collection :posts, :Post
|
7
|
+
end
|
8
|
+
|
9
|
+
class Post < Ohm::Model
|
10
|
+
attribute :title
|
11
|
+
attribute :body
|
12
|
+
|
13
|
+
unique :title
|
14
|
+
|
15
|
+
index :body
|
16
|
+
|
17
|
+
reference :user, :User
|
18
|
+
|
19
|
+
collection :likes, :Like
|
20
|
+
end
|
21
|
+
|
22
|
+
prepare do
|
23
|
+
Ohm.flush
|
24
|
+
end
|
25
|
+
|
26
|
+
setup do
|
27
|
+
user = User.create
|
28
|
+
|
29
|
+
[Ohm::Scope.new(Post, user_id: user.id), user]
|
30
|
+
end
|
31
|
+
|
32
|
+
test '#create saves model with overridden attributes' do |posts, user|
|
33
|
+
title = 'overrides references'
|
34
|
+
post = posts.create(title: title, user: User.create)
|
35
|
+
|
36
|
+
assert post.id
|
37
|
+
assert_equal post.title, title
|
38
|
+
assert_equal post.user, user
|
39
|
+
assert_equal post.user_id, user.id
|
40
|
+
|
41
|
+
title = 'overrides ids'
|
42
|
+
post = posts.create(title: title, user_id: User.create.id)
|
43
|
+
|
44
|
+
assert post.id
|
45
|
+
assert_equal post.title, title
|
46
|
+
assert_equal post.user, user
|
47
|
+
assert_equal post.user_id, user.id
|
48
|
+
end
|
49
|
+
|
50
|
+
test '#build initializes a new instance of klass arg' do |posts, user|
|
51
|
+
klass = OpenStruct
|
52
|
+
title = 'in scope'
|
53
|
+
post = posts.build(klass, title: title)
|
54
|
+
|
55
|
+
assert !post.id
|
56
|
+
assert_equal post.class, klass
|
57
|
+
assert_equal post.title, title
|
58
|
+
|
59
|
+
post = posts.build(title: title) # klass should default to model
|
60
|
+
|
61
|
+
assert !post.id
|
62
|
+
assert_equal post.class, Post
|
63
|
+
assert_equal post.title, title
|
64
|
+
end
|
65
|
+
|
66
|
+
test '#[] queries for single record by id in scope' do |posts, user|
|
67
|
+
post = posts.create(title: 'in scope')
|
68
|
+
|
69
|
+
assert_equal posts[post.id], post
|
70
|
+
|
71
|
+
post = Post.create(title: 'out of scope')
|
72
|
+
|
73
|
+
assert_equal posts[post.id], nil
|
74
|
+
end
|
75
|
+
|
76
|
+
test '#fetch queries for many records by id in scope' do |posts, user|
|
77
|
+
collection = []
|
78
|
+
|
79
|
+
2.times { |i| collection << posts.create(title: "in scope #{i}") }
|
80
|
+
|
81
|
+
assert_equal posts.fetch(collection.collect(&:id)), collection
|
82
|
+
|
83
|
+
collection << Post.create(title: 'out of scope')
|
84
|
+
|
85
|
+
assert posts.fetch(collection.collect(&:id)) != collection
|
86
|
+
end
|
87
|
+
|
88
|
+
test '#with queries single record by unique index in scope' do |posts, user|
|
89
|
+
title = 'in scope'
|
90
|
+
post = posts.create(title: title)
|
91
|
+
|
92
|
+
assert_equal posts.with(:title, title), post
|
93
|
+
|
94
|
+
title = 'out of scope'
|
95
|
+
Post.create(title: title)
|
96
|
+
|
97
|
+
assert_equal posts.with(:title, title), nil
|
98
|
+
end
|
99
|
+
|
100
|
+
test '#with raises for non-unique index' do |posts, user|
|
101
|
+
assert_raise(Ohm::IndexNotFound) do
|
102
|
+
posts.with(:body, 'body')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
test '#exists? checks if record with id exists in scope' do |posts, user|
|
107
|
+
post = posts.create(title: 'in scope')
|
108
|
+
|
109
|
+
assert posts.exists?(post.id)
|
110
|
+
|
111
|
+
post = Post.create(title: 'out of scope')
|
112
|
+
|
113
|
+
assert !posts.exists?(post.id)
|
114
|
+
end
|
115
|
+
|
116
|
+
test '#all returns all records in scope' do |posts, user|
|
117
|
+
2.times { |i| posts.create(title: "in scope #{i}") }
|
118
|
+
|
119
|
+
Post.create(title: 'out of scope')
|
120
|
+
|
121
|
+
assert posts.all.count > 0
|
122
|
+
|
123
|
+
posts.all.each do |post|
|
124
|
+
assert_equal post.user, user
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
test '#find queries for records by indexes in scope' do |posts, user|
|
129
|
+
body = 'correct body'
|
130
|
+
|
131
|
+
2.times { |i| posts.create(title: "in scope #{i}", body: body) }
|
132
|
+
|
133
|
+
posts.create(title: 'in scope wrong body', body: 'wrong')
|
134
|
+
|
135
|
+
Post.create(title: 'out of scope correct body', body: body)
|
136
|
+
|
137
|
+
posts.find(body: body).each do |post|
|
138
|
+
assert_equal post.body, body
|
139
|
+
assert_equal post.user, user
|
140
|
+
end
|
141
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ohm-scope
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steve Weiss
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-05-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ohm
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- weissst@mail.gvsu.edu
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gems"
|
35
|
+
- ".gitignore"
|
36
|
+
- LICENSE
|
37
|
+
- Makefile
|
38
|
+
- README.md
|
39
|
+
- lib/ohm/scope.rb
|
40
|
+
- ohm-scope.gemspec
|
41
|
+
- test/all.rb
|
42
|
+
homepage: https://github.com/sirscriptalot/ohm-scope
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
metadata: {}
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 2.6.11
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: Ohm::Scope
|
66
|
+
test_files: []
|