databound 2.0.1 → 3.0.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 +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
|

|
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
|