databound 2.0.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/databound.gemspec +3 -3
- data/lib/databound.rb +2 -29
- data/lib/databound/config.rb +38 -0
- data/lib/databound/controller.rb +63 -0
- data/lib/databound/data.rb +16 -10
- data/lib/databound/extensions.rb +0 -2
- data/lib/databound/manager.rb +72 -41
- data/lib/databound/rails/routes.rb +4 -3
- data/lib/databound/version.rb +1 -1
- data/spec/controllers/{permitted_columns_controller_spec.rb → columns_controller_spec.rb} +30 -1
- data/spec/controllers/model_controller_spec.rb +22 -0
- data/spec/controllers/permit_controller_spec.rb +124 -0
- data/spec/controllers/{permitted_routes_columns_controller_spec.rb → routes_opts_controller_spec.rb} +0 -0
- data/spec/internal/app/controllers/columns_controller.rb +6 -0
- data/spec/internal/app/controllers/dsl_controller.rb +8 -18
- data/spec/internal/app/controllers/loose_dsl_controller.rb +4 -13
- data/spec/internal/app/controllers/no_model_controller.rb +2 -1
- data/spec/internal/app/controllers/permit_controller.rb +18 -0
- data/spec/internal/app/controllers/users_controller.rb +3 -10
- data/spec/internal/config/routes.rb +5 -4
- metadata +20 -18
- data/lib/databound/utils.rb +0 -36
- data/spec/controllers/no_model_controller_spec.rb +0 -9
- data/spec/controllers/permit_update_destroy_controller_spec.rb +0 -66
- data/spec/internal/app/controllers/permit_update_destroy_controller.rb +0 -17
- data/spec/internal/app/controllers/permitted_columns_controller.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c17ea9293869f528bb57b8f815ade20dfe629904
|
4
|
+
data.tar.gz: bff389e7ba5502edb650bede3fe7b78acda3a88e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac22d42e131278f67c5fa25dbcfeeb3f8e8300c1596d47ddf5493cb19daab2a9cc3cd05519ed5a8b780ba703db670bffaea15144ac66ab364d9807bf6553eeb4
|
7
|
+
data.tar.gz: 2c4d73542e0f1195e9e1ebc0aebeb6cd366c2f77b526e347d160225694b4c03b15b4882ef5cb21b4c799c70fed73122996217084725fe44bdce56eabfa26631a
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
![Databound](https://cloud.githubusercontent.com/assets/1877286/4743542/df89dcec-5a28-11e4-9114-6f383fe269cb.png)
|
8
8
|
|
9
|
-
|
9
|
+
Provides Javascript a simple CRUD API to the Ruby on Rails backend.
|
10
10
|
|
11
11
|
This repo is for Ruby on Rails backend part of Databound.
|
12
12
|
|
@@ -24,11 +24,11 @@ This repo is for Ruby on Rails backend part of Databound.
|
|
24
24
|
});
|
25
25
|
|
26
26
|
User.find(15).then(function(user) {
|
27
|
-
|
27
|
+
alert('User no. 15: ' + user.name);
|
28
28
|
});
|
29
29
|
|
30
30
|
User.create({ name: 'Peter' }).then(function(user) {
|
31
|
-
|
31
|
+
alert('I am ' + user.name + ' from database');
|
32
32
|
});
|
33
33
|
```
|
34
34
|
|
data/databound.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Databound::VERSION
|
9
9
|
spec.authors = ['Domas Bitvinskas']
|
10
10
|
spec.email = ['domas.bitvinskas@me.com']
|
11
|
-
spec.summary = %q{
|
12
|
-
spec.description = %q{
|
13
|
-
spec.homepage = '
|
11
|
+
spec.summary = %q{Provides Javascript a simple API to the Ruby on Rails CRUD}
|
12
|
+
spec.description = %q{It lets you use methods like create, update, destroy in the front-end while handling all the setup and providing basic security out of the box.}
|
13
|
+
spec.homepage = 'http://databound.me'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
data/lib/databound.rb
CHANGED
@@ -2,13 +2,13 @@ require 'databound/extensions'
|
|
2
2
|
require 'databound/version'
|
3
3
|
require 'databound/data'
|
4
4
|
require 'databound/manager'
|
5
|
-
require 'databound/
|
5
|
+
require 'databound/config'
|
6
|
+
require 'databound/controller'
|
6
7
|
require 'databound/rails/routes'
|
7
8
|
|
8
9
|
module Databound
|
9
10
|
def self.included(base)
|
10
11
|
base.send(:before_action, :init_crud, only: %i(where create update destroy))
|
11
|
-
base.extend(ClassMethods)
|
12
12
|
end
|
13
13
|
|
14
14
|
def where
|
@@ -66,14 +66,6 @@ module Databound
|
|
66
66
|
serializer.new(record).attributes[:id]
|
67
67
|
end
|
68
68
|
|
69
|
-
def model
|
70
|
-
raise 'Override model method to specify a model to be used in CRUD'
|
71
|
-
end
|
72
|
-
|
73
|
-
def permitted_columns
|
74
|
-
[]
|
75
|
-
end
|
76
|
-
|
77
69
|
def init_crud
|
78
70
|
@crud = Databound::Manager.new(self)
|
79
71
|
end
|
@@ -81,23 +73,4 @@ module Databound
|
|
81
73
|
def scoped_records
|
82
74
|
@crud.find_scoped_records(only_extra_scopes: true)
|
83
75
|
end
|
84
|
-
|
85
|
-
module ClassMethods
|
86
|
-
attr_reader :dsls
|
87
|
-
attr_reader :stricts
|
88
|
-
attr_reader :permit_update_destroy
|
89
|
-
|
90
|
-
def dsl(name, value, strict: true, &block)
|
91
|
-
@stricts ||= {}
|
92
|
-
@stricts[name.to_s] = strict
|
93
|
-
|
94
|
-
@dsls ||= {}
|
95
|
-
@dsls[name.to_s] ||= {}
|
96
|
-
@dsls[name.to_s][value.to_s] = block
|
97
|
-
end
|
98
|
-
|
99
|
-
def permit_update_destroy?(&block)
|
100
|
-
@permit_update_destroy = block
|
101
|
-
end
|
102
|
-
end
|
103
76
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Databound
|
2
|
+
class Config
|
3
|
+
def initialize(block, model)
|
4
|
+
@model = model
|
5
|
+
@permit = {}
|
6
|
+
instance_eval(&block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def columns(*specified_columns)
|
10
|
+
@columns = specified_columns
|
11
|
+
end
|
12
|
+
|
13
|
+
def model(specified_model)
|
14
|
+
raise "Model '#{@model}' already specified" if @model
|
15
|
+
|
16
|
+
@model = specified_model
|
17
|
+
end
|
18
|
+
|
19
|
+
def permit(*methods, &block)
|
20
|
+
methods.each do |method|
|
21
|
+
@permit[method] = block
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def dsl(name, value, strict: true, &block)
|
26
|
+
@stricts ||= {}
|
27
|
+
@stricts[name] = strict
|
28
|
+
|
29
|
+
@dsls ||= {}
|
30
|
+
@dsls[name] ||= {}
|
31
|
+
@dsls[name][value.to_s] = block
|
32
|
+
end
|
33
|
+
|
34
|
+
def read(name)
|
35
|
+
instance_variable_get("@#{name}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Databound
|
2
|
+
class Controller
|
3
|
+
class << self
|
4
|
+
def add_application_controller_configs!
|
5
|
+
def ApplicationController.databound(model = nil, &block)
|
6
|
+
include Databound
|
7
|
+
|
8
|
+
send(:define_method, :databound_config) do
|
9
|
+
Databound::Config.new(block, model)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_or_create(name, resource, opts)
|
15
|
+
find(name) || create(name, resource, opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
def create(name, resource, opts)
|
19
|
+
opts ||= {}
|
20
|
+
Object.const_set(as_constant_string(name), new(resource, opts))
|
21
|
+
end
|
22
|
+
|
23
|
+
def new(resource, opts)
|
24
|
+
model_name = opts.delete(:model) || fallback_model(resource)
|
25
|
+
|
26
|
+
result = Class.new(ApplicationController)
|
27
|
+
result.send(:databound) do
|
28
|
+
model model_name
|
29
|
+
|
30
|
+
opts.each do |key, value|
|
31
|
+
send(key, *value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
def fallback_model(resource)
|
39
|
+
resource.to_s.classify.underscore.to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def exists?(path)
|
43
|
+
name_error = false
|
44
|
+
|
45
|
+
begin
|
46
|
+
as_constant_string(path).constantize
|
47
|
+
rescue NameError
|
48
|
+
name_error = true
|
49
|
+
end
|
50
|
+
|
51
|
+
!name_error
|
52
|
+
end
|
53
|
+
|
54
|
+
def find(path)
|
55
|
+
as_constant_string(path).constantize if exists?(path)
|
56
|
+
end
|
57
|
+
|
58
|
+
def as_constant_string(name)
|
59
|
+
"#{name.camelize}Controller"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/databound/data.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
module Databound
|
2
2
|
class Data
|
3
|
-
def initialize(controller, json)
|
3
|
+
def initialize(controller, json, model)
|
4
4
|
return unless json
|
5
5
|
|
6
6
|
@controller = controller
|
7
|
-
@
|
8
|
-
@params = json if json.is_a?(Hash)
|
7
|
+
@json = json
|
9
8
|
@data = interpolated_params
|
9
|
+
@model = model
|
10
10
|
end
|
11
11
|
|
12
|
-
def records
|
13
|
-
model.where(@data)
|
12
|
+
def records
|
13
|
+
@model.where(@data)
|
14
14
|
end
|
15
15
|
|
16
16
|
def to_h
|
@@ -19,21 +19,27 @@ module Databound
|
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
|
+
def params
|
23
|
+
@params ||= JSON.parse(@json) if @json.is_a?(String)
|
24
|
+
@params ||= @json if @json.is_a?(Hash)
|
25
|
+
OpenStruct.new(@params)
|
26
|
+
end
|
27
|
+
|
22
28
|
def interpolated_params
|
23
|
-
|
29
|
+
params.to_h.each_with_object({}) do |(key, val), obj|
|
24
30
|
check_strict!(key, val)
|
25
31
|
|
26
32
|
block = dsl_block(key, val)
|
27
|
-
obj[key] = block ? @controller.instance_exec(
|
33
|
+
obj[key] = block ? @controller.instance_exec(params, &block) : val
|
28
34
|
end
|
29
35
|
end
|
30
36
|
|
31
37
|
def dsl_block(key, val)
|
32
|
-
swallow_nil { dsl_key(key)[val] }
|
38
|
+
swallow_nil { dsl_key(key)[val.to_s] }
|
33
39
|
end
|
34
40
|
|
35
41
|
def dsl_key(key)
|
36
|
-
swallow_nil { @controller.
|
42
|
+
swallow_nil { @controller.databound_config.read(:dsls)[key] }
|
37
43
|
end
|
38
44
|
|
39
45
|
def check_strict!(key, val)
|
@@ -45,7 +51,7 @@ module Databound
|
|
45
51
|
end
|
46
52
|
|
47
53
|
def strict?(key)
|
48
|
-
swallow_nil { @controller.
|
54
|
+
swallow_nil { @controller.databound_config.read(:stricts)[key] }
|
49
55
|
end
|
50
56
|
end
|
51
57
|
end
|
data/lib/databound/extensions.rb
CHANGED
data/lib/databound/manager.rb
CHANGED
@@ -3,67 +3,78 @@ module Databound
|
|
3
3
|
class Manager
|
4
4
|
def initialize(controller)
|
5
5
|
@controller = controller
|
6
|
-
@model = @controller.send(:model)
|
7
6
|
|
8
|
-
@scope = Databound::Data.new(controller, scope_js)
|
9
|
-
@data = Databound::Data.new(controller, data_js)
|
7
|
+
@scope = Databound::Data.new(@controller, scope_js, model)
|
8
|
+
@data = Databound::Data.new(@controller, data_js, model)
|
10
9
|
|
11
10
|
@extra_where_scopes = JSON.parse(extra_where_scopes_js).map do |extra_scope|
|
12
|
-
Databound::Data.new(controller, extra_scope)
|
11
|
+
Databound::Data.new(@controller, extra_scope, model)
|
13
12
|
end
|
14
13
|
end
|
15
14
|
|
16
15
|
def find_scoped_records(only_extra_scopes: false)
|
17
|
-
records =
|
18
|
-
records
|
16
|
+
records = model.where(or_query(@scope, *@extra_where_scopes))
|
17
|
+
records = filter_by_params!(records) unless only_extra_scopes
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
if only_extra_scopes
|
25
|
-
records.flatten
|
26
|
-
else
|
27
|
-
records.map { |record| record.where(@data) }.flatten
|
28
|
-
end
|
19
|
+
check_permit!(:read, params, records)
|
20
|
+
records
|
29
21
|
end
|
30
22
|
|
31
23
|
def create_from_data
|
32
|
-
check_params!
|
33
|
-
|
24
|
+
check_params!(:create)
|
25
|
+
record = model.new(params.to_h)
|
26
|
+
check_permit!(:create, params, record)
|
27
|
+
|
28
|
+
record.save
|
29
|
+
record
|
34
30
|
end
|
35
31
|
|
36
32
|
def update_from_data
|
37
|
-
|
33
|
+
attributes = params.to_h
|
34
|
+
id = attributes.delete(:id)
|
38
35
|
|
39
|
-
check_params!
|
40
|
-
record =
|
41
|
-
|
42
|
-
record.update(@data)
|
36
|
+
check_params!(:update)
|
37
|
+
record = model.find(id)
|
38
|
+
check_permit!(:update, params, record)
|
43
39
|
|
40
|
+
record.update(attributes)
|
44
41
|
record
|
45
42
|
end
|
46
43
|
|
47
44
|
def destroy_from_data
|
48
|
-
record =
|
49
|
-
|
45
|
+
record = model.find(params.id)
|
46
|
+
check_permit!(:destroy, params, record)
|
50
47
|
record.destroy
|
51
48
|
end
|
52
49
|
|
53
50
|
private
|
54
51
|
|
55
|
-
def
|
56
|
-
|
52
|
+
def or_query(*scopes)
|
53
|
+
nodes = scopes.map do |scope|
|
54
|
+
model.where(scope.to_h).where_values.reduce(:and)
|
55
|
+
end
|
56
|
+
|
57
|
+
nodes[1..-1].reduce(nodes.first) do |memo, node|
|
58
|
+
node.or(memo)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def check_params!(action)
|
63
|
+
@action = action
|
64
|
+
return if columns == :all
|
57
65
|
return if unpermitted_columns.empty?
|
58
66
|
|
59
67
|
raise NotPermittedError, "Request includes unpermitted columns: #{unpermitted_columns.join(', ')}"
|
60
68
|
end
|
61
69
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
70
|
+
def check_permit!(method, params, record = nil)
|
71
|
+
permit_checks = @controller.databound_config.read(:permit)
|
72
|
+
check = permit_checks[method]
|
73
|
+
|
74
|
+
return unless check
|
75
|
+
return if @controller.instance_exec(params, record, &check)
|
65
76
|
|
66
|
-
raise NotPermittedError,
|
77
|
+
raise NotPermittedError, "Request for #{method} not permitted"
|
67
78
|
end
|
68
79
|
|
69
80
|
def permit_update_destroy_block
|
@@ -71,29 +82,35 @@ module Databound
|
|
71
82
|
end
|
72
83
|
|
73
84
|
def unpermitted_columns
|
74
|
-
|
75
|
-
requested - permitted_columns.map(&:to_s)
|
85
|
+
params.to_h.keys - columns - allowed_action_columns
|
76
86
|
end
|
77
87
|
|
78
|
-
def
|
79
|
-
|
88
|
+
def params
|
89
|
+
OpenStruct.new(@scope.to_h.merge(@data.to_h))
|
90
|
+
end
|
91
|
+
|
92
|
+
def allowed_action_columns
|
93
|
+
@action == :update ? [:id] : []
|
94
|
+
end
|
80
95
|
|
81
|
-
|
82
|
-
|
96
|
+
def columns
|
97
|
+
result = @controller.databound_config.read(:columns)
|
98
|
+
|
99
|
+
case result
|
100
|
+
when [:all]
|
83
101
|
:all
|
84
|
-
when :table_columns
|
102
|
+
when [:table_columns]
|
85
103
|
table_columns
|
86
104
|
else
|
87
|
-
Array(
|
105
|
+
Array(result)
|
88
106
|
end
|
89
107
|
end
|
90
108
|
|
91
109
|
def table_columns
|
92
|
-
# permit all by default
|
93
110
|
if mongoid?
|
94
111
|
model.fields.keys.map(&:to_sym)
|
95
112
|
elsif activerecord?
|
96
|
-
model.column_names
|
113
|
+
model.column_names.map(&:to_sym)
|
97
114
|
else
|
98
115
|
raise 'ORM not supported. Use ActiveRecord or Mongoid'
|
99
116
|
end
|
@@ -108,7 +125,13 @@ module Databound
|
|
108
125
|
end
|
109
126
|
|
110
127
|
def model
|
111
|
-
|
128
|
+
raise 'No model specified' unless model_name
|
129
|
+
|
130
|
+
model_name.to_s.camelize.constantize
|
131
|
+
end
|
132
|
+
|
133
|
+
def model_name
|
134
|
+
@controller.databound_config.read(:model)
|
112
135
|
end
|
113
136
|
|
114
137
|
def scope_js
|
@@ -122,5 +145,13 @@ module Databound
|
|
122
145
|
def extra_where_scopes_js
|
123
146
|
@controller.params[:extra_where_scopes] || '[]'
|
124
147
|
end
|
148
|
+
|
149
|
+
def extra_scope_records
|
150
|
+
@extra_where_scopes.flat_map(&:records)
|
151
|
+
end
|
152
|
+
|
153
|
+
def filter_by_params!(records)
|
154
|
+
records.where(params.to_h)
|
155
|
+
end
|
125
156
|
end
|
126
157
|
end
|
@@ -3,15 +3,16 @@ class ActionDispatch::Routing::Mapper
|
|
3
3
|
namespace = @scope[:path]
|
4
4
|
namespace = namespace[1..-1] if namespace
|
5
5
|
opts = resources.pop if resources.last.is_a?(Hash)
|
6
|
+
Databound::Controller.add_application_controller_configs!
|
6
7
|
|
7
8
|
resources.each do |resource|
|
8
9
|
Rails.application.routes.draw do
|
9
|
-
|
10
|
-
Databound::
|
10
|
+
controller_name = [namespace, resource].compact.join('/')
|
11
|
+
Databound::Controller.find_or_create(controller_name, resource, opts)
|
11
12
|
|
12
13
|
%i(where create update destroy).each do |name|
|
13
14
|
path = [namespace, resource, name].compact.join('/')
|
14
|
-
to = [
|
15
|
+
to = [controller_name, name].join('#')
|
15
16
|
post path => to
|
16
17
|
end
|
17
18
|
end
|
data/lib/databound/version.rb
CHANGED
@@ -1,6 +1,35 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe PostsController, type: :controller do
|
4
|
+
describe 'via routes' do
|
5
|
+
it 'raise when param is not permitted' do
|
6
|
+
data = {
|
7
|
+
data: {
|
8
|
+
city: 'Barcelona',
|
9
|
+
},
|
10
|
+
scope: {},
|
11
|
+
}
|
12
|
+
|
13
|
+
expect { post(:create, javascriptize(data)) }.to raise_error(
|
14
|
+
Databound::NotPermittedError,
|
15
|
+
'Request includes unpermitted columns: city',
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should create when param is permitted' do
|
20
|
+
data = {
|
21
|
+
data: {
|
22
|
+
title: 'Nikki',
|
23
|
+
},
|
24
|
+
scope: {},
|
25
|
+
}
|
26
|
+
|
27
|
+
expect { post(:create, javascriptize(data)) }.not_to raise_error
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe ColumnsController, type: :controller do
|
4
33
|
describe '#create' do
|
5
34
|
it 'raise when param is not permitted' do
|
6
35
|
data = {
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ProjectsController do
|
4
|
+
describe 'via routes' do
|
5
|
+
it 'should be able to specify model via routes' do
|
6
|
+
expect(ProjectsController.new.databound_config.read(:model)).to equal(:user)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should be able to specify columns via routes' do
|
10
|
+
expect(ProjectsController.new.databound_config.read(:columns))
|
11
|
+
.to eql(%i(city user_id))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe NoModelController, type: :controller do
|
17
|
+
describe 'raise error' do
|
18
|
+
it 'when model is not defined' do
|
19
|
+
expect { post(:create) }.to raise_error(RuntimeError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PermitController, type: :controller do
|
4
|
+
CURRENT_USER_ID = 1
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
Project.create(city: 'LA', user_id: 5)
|
8
|
+
Project.create(city: 'LA', user_id: 1)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#read' do
|
12
|
+
it 'raise when scope is not permitted' do
|
13
|
+
data = {
|
14
|
+
data: {
|
15
|
+
city: 'LA',
|
16
|
+
dont_permit: true,
|
17
|
+
},
|
18
|
+
scope: {},
|
19
|
+
}
|
20
|
+
|
21
|
+
expect { post(:where, javascriptize(data)) }.to raise_error(
|
22
|
+
Databound::NotPermittedError,
|
23
|
+
'Request for read not permitted',
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should update when param is permitted' do
|
28
|
+
data = {
|
29
|
+
data: {
|
30
|
+
city: 'Barcelona',
|
31
|
+
user_id: 1,
|
32
|
+
},
|
33
|
+
scope: {},
|
34
|
+
}
|
35
|
+
|
36
|
+
expect { post(:where, javascriptize(data)) }.not_to raise_error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#create' do
|
41
|
+
it 'raise when scope is not permitted' do
|
42
|
+
data = {
|
43
|
+
data: {
|
44
|
+
city: 'Barcelona',
|
45
|
+
user_id: 2,
|
46
|
+
},
|
47
|
+
scope: {},
|
48
|
+
}
|
49
|
+
|
50
|
+
expect { post(:create, javascriptize(data)) }.to raise_error(
|
51
|
+
Databound::NotPermittedError,
|
52
|
+
'Request for create not permitted',
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should update when param is permitted' do
|
57
|
+
data = {
|
58
|
+
data: {
|
59
|
+
city: 'Barcelona',
|
60
|
+
user_id: 1,
|
61
|
+
},
|
62
|
+
scope: {},
|
63
|
+
}
|
64
|
+
|
65
|
+
expect { post(:create, javascriptize(data)) }.not_to raise_error
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#update' do
|
70
|
+
it 'raise when scope is not permitted' do
|
71
|
+
data = {
|
72
|
+
data: {
|
73
|
+
id: 1,
|
74
|
+
city: 'Barcelona',
|
75
|
+
},
|
76
|
+
scope: {},
|
77
|
+
}
|
78
|
+
|
79
|
+
expect { post(:update, javascriptize(data)) }.to raise_error(
|
80
|
+
Databound::NotPermittedError,
|
81
|
+
'Request for update not permitted',
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should update when param is permitted' do
|
86
|
+
data = {
|
87
|
+
data: {
|
88
|
+
id: 2,
|
89
|
+
city: 'Barcelona',
|
90
|
+
},
|
91
|
+
scope: {},
|
92
|
+
}
|
93
|
+
|
94
|
+
expect { post(:update, javascriptize(data)) }.not_to raise_error
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#destroy' do
|
99
|
+
it 'raise when scope is not permitted' do
|
100
|
+
data = {
|
101
|
+
data: {
|
102
|
+
id: 1,
|
103
|
+
},
|
104
|
+
scope: {},
|
105
|
+
}
|
106
|
+
|
107
|
+
expect { post(:destroy, javascriptize(data)) }.to raise_error(
|
108
|
+
Databound::NotPermittedError,
|
109
|
+
'Request for destroy not permitted',
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should destroy when param is permitted' do
|
114
|
+
data = {
|
115
|
+
data: {
|
116
|
+
id: 2,
|
117
|
+
},
|
118
|
+
scope: {},
|
119
|
+
}
|
120
|
+
|
121
|
+
expect { post(:destroy, javascriptize(data)) }.not_to raise_error
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/spec/controllers/{permitted_routes_columns_controller_spec.rb → routes_opts_controller_spec.rb}
RENAMED
File without changes
|
@@ -1,21 +1,11 @@
|
|
1
1
|
class DslController < ApplicationController
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def permitted_columns
|
11
|
-
%i(name city)
|
12
|
-
end
|
13
|
-
|
14
|
-
dsl(:city, :hottest) do
|
15
|
-
'Miami'
|
16
|
-
end
|
17
|
-
|
18
|
-
dsl(:city, :coldest) do |params|
|
19
|
-
"Where #{params[:name]} lives"
|
2
|
+
databound do
|
3
|
+
model :user
|
4
|
+
columns :name, :city
|
5
|
+
|
6
|
+
dsl(:city, :hottest) { 'Miami' }
|
7
|
+
dsl(:city, :coldest) do |params|
|
8
|
+
"Where #{params.name} lives"
|
9
|
+
end
|
20
10
|
end
|
21
11
|
end
|
@@ -1,17 +1,8 @@
|
|
1
1
|
class LooseDslController < ApplicationController
|
2
|
-
|
2
|
+
databound do
|
3
|
+
model :user
|
4
|
+
columns :name, :city
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
def permitted_columns
|
7
|
-
%i(name city)
|
8
|
-
end
|
9
|
-
|
10
|
-
def model
|
11
|
-
User
|
12
|
-
end
|
13
|
-
|
14
|
-
dsl(:city, :hottest, strict: false) do
|
15
|
-
'Miami'
|
6
|
+
dsl(:city, :hottest, strict: false) { 'Miami' }
|
16
7
|
end
|
17
8
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class PermitController < ApplicationController
|
2
|
+
databound do
|
3
|
+
model :project
|
4
|
+
columns :name, :city, :user_id, :dont_permit
|
5
|
+
|
6
|
+
permit(:read) do |params, records|
|
7
|
+
!params.dont_permit
|
8
|
+
end
|
9
|
+
|
10
|
+
permit(:create) do |params|
|
11
|
+
params.user_id == CURRENT_USER_ID
|
12
|
+
end
|
13
|
+
|
14
|
+
permit(:update, :destroy) do |_, record|
|
15
|
+
record.user_id == CURRENT_USER_ID
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
2
|
databound :users
|
3
3
|
databound :no_model
|
4
|
-
databound :
|
4
|
+
databound :columns
|
5
5
|
databound :dsl
|
6
6
|
databound :loose_dsl
|
7
|
-
databound :messages,
|
8
|
-
databound :
|
9
|
-
databound :posts,
|
7
|
+
databound :messages, columns: :table_columns
|
8
|
+
databound :permit
|
9
|
+
databound :posts, columns: %i(title)
|
10
|
+
databound :projects, columns: %i(city user_id), model: :user
|
10
11
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: databound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Domas Bitvinskas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec-rails
|
@@ -122,7 +122,8 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '10.0'
|
125
|
-
description:
|
125
|
+
description: It lets you use methods like create, update, destroy in the front-end
|
126
|
+
while handling all the setup and providing basic security out of the box.
|
126
127
|
email:
|
127
128
|
- domas.bitvinskas@me.com
|
128
129
|
executables: []
|
@@ -138,29 +139,30 @@ files:
|
|
138
139
|
- config.ru
|
139
140
|
- databound.gemspec
|
140
141
|
- lib/databound.rb
|
142
|
+
- lib/databound/config.rb
|
143
|
+
- lib/databound/controller.rb
|
141
144
|
- lib/databound/data.rb
|
142
145
|
- lib/databound/extensions.rb
|
143
146
|
- lib/databound/manager.rb
|
144
147
|
- lib/databound/rails/routes.rb
|
145
|
-
- lib/databound/utils.rb
|
146
148
|
- lib/databound/version.rb
|
147
149
|
- lib/generators/databound/install/install_generator.rb
|
148
150
|
- lib/generators/databound/install/templates/application.js
|
149
151
|
- lib/generators/databound/install/templates/databound-standalone.js
|
152
|
+
- spec/controllers/columns_controller_spec.rb
|
150
153
|
- spec/controllers/databound_spec.rb
|
151
154
|
- spec/controllers/dsl_controller_spec.rb
|
152
155
|
- spec/controllers/loose_dsl_controller_spec.rb
|
153
|
-
- spec/controllers/
|
156
|
+
- spec/controllers/model_controller_spec.rb
|
154
157
|
- spec/controllers/on_the_fly_spec.rb
|
155
|
-
- spec/controllers/
|
156
|
-
- spec/controllers/
|
157
|
-
- spec/controllers/permitted_routes_columns_controller_spec.rb
|
158
|
+
- spec/controllers/permit_controller_spec.rb
|
159
|
+
- spec/controllers/routes_opts_controller_spec.rb
|
158
160
|
- spec/internal/app/controllers/application_controller.rb
|
161
|
+
- spec/internal/app/controllers/columns_controller.rb
|
159
162
|
- spec/internal/app/controllers/dsl_controller.rb
|
160
163
|
- spec/internal/app/controllers/loose_dsl_controller.rb
|
161
164
|
- spec/internal/app/controllers/no_model_controller.rb
|
162
|
-
- spec/internal/app/controllers/
|
163
|
-
- spec/internal/app/controllers/permitted_columns_controller.rb
|
165
|
+
- spec/internal/app/controllers/permit_controller.rb
|
164
166
|
- spec/internal/app/controllers/users_controller.rb
|
165
167
|
- spec/internal/app/models/message.rb
|
166
168
|
- spec/internal/app/models/post.rb
|
@@ -222,7 +224,7 @@ files:
|
|
222
224
|
- spec/support/rails_test_app/public/robots.txt
|
223
225
|
- spec/support/rails_test_app/vendor/assets/javascripts/.keep
|
224
226
|
- spec/support/rails_test_app/vendor/assets/stylesheets/.keep
|
225
|
-
homepage:
|
227
|
+
homepage: http://databound.me
|
226
228
|
licenses:
|
227
229
|
- MIT
|
228
230
|
metadata: {}
|
@@ -245,22 +247,22 @@ rubyforge_project:
|
|
245
247
|
rubygems_version: 2.2.2
|
246
248
|
signing_key:
|
247
249
|
specification_version: 4
|
248
|
-
summary:
|
250
|
+
summary: Provides Javascript a simple API to the Ruby on Rails CRUD
|
249
251
|
test_files:
|
252
|
+
- spec/controllers/columns_controller_spec.rb
|
250
253
|
- spec/controllers/databound_spec.rb
|
251
254
|
- spec/controllers/dsl_controller_spec.rb
|
252
255
|
- spec/controllers/loose_dsl_controller_spec.rb
|
253
|
-
- spec/controllers/
|
256
|
+
- spec/controllers/model_controller_spec.rb
|
254
257
|
- spec/controllers/on_the_fly_spec.rb
|
255
|
-
- spec/controllers/
|
256
|
-
- spec/controllers/
|
257
|
-
- spec/controllers/permitted_routes_columns_controller_spec.rb
|
258
|
+
- spec/controllers/permit_controller_spec.rb
|
259
|
+
- spec/controllers/routes_opts_controller_spec.rb
|
258
260
|
- spec/internal/app/controllers/application_controller.rb
|
261
|
+
- spec/internal/app/controllers/columns_controller.rb
|
259
262
|
- spec/internal/app/controllers/dsl_controller.rb
|
260
263
|
- spec/internal/app/controllers/loose_dsl_controller.rb
|
261
264
|
- spec/internal/app/controllers/no_model_controller.rb
|
262
|
-
- spec/internal/app/controllers/
|
263
|
-
- spec/internal/app/controllers/permitted_columns_controller.rb
|
265
|
+
- spec/internal/app/controllers/permit_controller.rb
|
264
266
|
- spec/internal/app/controllers/users_controller.rb
|
265
267
|
- spec/internal/app/models/message.rb
|
266
268
|
- spec/internal/app/models/post.rb
|
data/lib/databound/utils.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
module Databound
|
2
|
-
class Utils
|
3
|
-
def self.create_controller_unless_exists(path, resource, opts)
|
4
|
-
return if exists?(path)
|
5
|
-
opts ||= {}
|
6
|
-
|
7
|
-
controller = Class.new(ApplicationController)
|
8
|
-
controller.send(:include, Databound)
|
9
|
-
controller.send(:define_method, :model) do
|
10
|
-
resource.to_s.classify.constantize
|
11
|
-
end
|
12
|
-
|
13
|
-
controller.send(:define_method, :permitted_columns) do
|
14
|
-
opts.fetch(:permitted_columns) do
|
15
|
-
raise 'Specify permitted_columns in routes or the controller'
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
Object.const_set(controller_name(path), controller)
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.exists?(path)
|
23
|
-
begin
|
24
|
-
controller_name(path).constantize
|
25
|
-
rescue NameError
|
26
|
-
return false
|
27
|
-
end
|
28
|
-
|
29
|
-
true
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.controller_name(path)
|
33
|
-
"#{path.camelize}Controller"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe PermitUpdateDestroyController, type: :controller do
|
4
|
-
CURRENT_USER_ID = 1
|
5
|
-
|
6
|
-
before :each do
|
7
|
-
Project.create(city: 'LA', user_id: 5)
|
8
|
-
Project.create(city: 'LA', user_id: 1)
|
9
|
-
end
|
10
|
-
|
11
|
-
describe '#update' do
|
12
|
-
it 'raise when scope is not permitted' do
|
13
|
-
data = {
|
14
|
-
data: {
|
15
|
-
id: 1,
|
16
|
-
city: 'Barcelona',
|
17
|
-
},
|
18
|
-
scope: {},
|
19
|
-
}
|
20
|
-
|
21
|
-
expect { post(:update, javascriptize(data)) }.to raise_error(
|
22
|
-
Databound::NotPermittedError,
|
23
|
-
'Request for update or destroy not permitted',
|
24
|
-
)
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'should update when param is permitted' do
|
28
|
-
data = {
|
29
|
-
data: {
|
30
|
-
id: 2,
|
31
|
-
city: 'Barcelona',
|
32
|
-
},
|
33
|
-
scope: {},
|
34
|
-
}
|
35
|
-
|
36
|
-
expect { post(:update, javascriptize(data)) }.not_to raise_error
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe '#destroy' do
|
41
|
-
it 'raise when scope is not permitted' do
|
42
|
-
data = {
|
43
|
-
data: {
|
44
|
-
id: 1,
|
45
|
-
},
|
46
|
-
scope: {},
|
47
|
-
}
|
48
|
-
|
49
|
-
expect { post(:destroy, javascriptize(data)) }.to raise_error(
|
50
|
-
Databound::NotPermittedError,
|
51
|
-
'Request for update or destroy not permitted',
|
52
|
-
)
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'should destroy when param is permitted' do
|
56
|
-
data = {
|
57
|
-
data: {
|
58
|
-
id: 2,
|
59
|
-
},
|
60
|
-
scope: {},
|
61
|
-
}
|
62
|
-
|
63
|
-
expect { post(:destroy, javascriptize(data)) }.not_to raise_error
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
class PermitUpdateDestroyController < ApplicationController
|
2
|
-
include Databound
|
3
|
-
|
4
|
-
private
|
5
|
-
|
6
|
-
def permitted_columns
|
7
|
-
%i(name city)
|
8
|
-
end
|
9
|
-
|
10
|
-
def model
|
11
|
-
Project
|
12
|
-
end
|
13
|
-
|
14
|
-
permit_update_destroy? do |record|
|
15
|
-
record.user_id == CURRENT_USER_ID
|
16
|
-
end
|
17
|
-
end
|