wire-framework 0.1.4.26 → 0.1.5

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.
data/README.md CHANGED
@@ -4,3 +4,19 @@ Wire is a DSL and Rack interface for quickly building web applications, without
4
4
  # Documentation
5
5
 
6
6
  [Wire RubyDoc](http://www.rubydoc.info/github/DataDrake/Wire)
7
+
8
+ ## License
9
+
10
+ Copyright 2017 Bryan T. Meyers
11
+
12
+ Licensed under the Apache License, Version 2.0 (the "License");
13
+ you may not use this file except in compliance with the License.
14
+ You may obtain a copy of the License at
15
+
16
+ http://www.apache.org/licenses/LICENSE-2.0
17
+
18
+ Unless required by applicable law or agreed to in writing, software
19
+ distributed under the License is distributed on an "AS IS" BASIS,
20
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ See the License for the specific language governing permissions and
22
+ limitations under the License.
data/lib/app.rb CHANGED
@@ -1,24 +1,43 @@
1
+ ##
2
+ # Copyright 2017 Bryan T. Meyers
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ##
16
+
17
+ require_relative 'closet/config'
18
+
1
19
  module Wire
2
- # App is a DSL function for mapping sub-URI to Wire::App(s)
3
- # @author Bryan T. Meyers
4
- module App
20
+ # App is a a REST endpoint for a Wire service
21
+ # @author Bryan T. Meyers
22
+ module App
23
+
24
+ # Callback for handling configs
25
+ # @param [Hash] conf the raw configuration
26
+ # @return [Hash] post-processed configuration
27
+ def self.configure(conf)
28
+ conf['type'] = Object.const_get(conf['type'])
29
+ if conf['type'].respond_to? :configure
30
+ conf = conf['type'].configure(conf)
31
+ end
32
+ conf
33
+ end
5
34
 
6
- # Setup an App
7
- # @param [String] base_uri the sub-URI
8
- # @param [Module] type the Wire::App
9
- # @param [Proc] block for configuring this App
10
- # @return [void]
11
- def app(base_uri, type, &block)
12
- $current_uri = base_uri
13
- $apps[base_uri] = { type: type, resources: {} }
14
- $current_app = $apps[base_uri]
15
- if ENV['RACK_ENV'].eql? 'development'
16
- $stderr.puts "Starting App at: /#{base_uri}"
17
- $stderr.puts 'Setting up resources...'
18
- end
19
- Docile.dsl_eval(type, &block)
20
- end
21
- end
35
+ # Read all of the configs in './configs/apps'
36
+ # @return [void]
37
+ def self.read_configs
38
+ $wire_apps = Wire::Config.read_config_dir('config/apps', method(:configure))
39
+ end
40
+ end
22
41
  end
23
42
 
24
43
  require_relative 'app/cache'
data/lib/app/cache.rb CHANGED
@@ -1,96 +1,105 @@
1
- require_relative '../app'
2
- require_relative '../closet/resource'
3
- require_relative '../app/render'
1
+ ##
2
+ # Copyright 2017 Bryan T. Meyers
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ##
4
16
 
5
17
  require 'lmdb'
6
18
 
7
19
  module Cache
8
- module Memory
9
- include Wire::App
10
- include Wire::Resource
11
- extend Render
20
+ module Memory
12
21
 
13
- $cache = {}
22
+ $cache = {}
14
23
 
15
- def self.update_cached(context)
16
- uri = context.uri.join('/')
17
- all = context.uri[0..2].join('/')
18
- env = $cache[context.app[:remote_uri]]
19
- db = env.database
20
- if context.uri[3]
21
- result = forward(:read,context)
22
- else
23
- result = forward(:readAll,context)
24
- end
25
- if result[0] == 200
26
- env.transaction do
27
- if context.action == :delete
28
- if db[uri]
29
- db.delete(uri)
30
- end
31
- else
32
- db[uri] = result[2]
33
- end
34
- end
35
- end
36
- if [:create,:update,:delete].include? context.action
37
- thing = forward(:readAll,context)
38
- if thing[0] == 200
39
- env.transaction do
40
- db[all] = thing[2]
41
- end
42
- end
43
- end
44
- result
45
- end
24
+ def self.update_cached(context)
25
+ uri = context.uri.join('/')
26
+ all = context.uri[0..2].join('/')
27
+ env = $cache[context.config['remote']]
28
+ db = env.database
29
+ if context.id
30
+ result = context.forward(:read)
31
+ else
32
+ result = context.forward(:readAll)
33
+ end
34
+ if result[0] == 200
35
+ env.transaction do
36
+ if context.action == :delete
37
+ if db[uri]
38
+ db.delete(uri)
39
+ end
40
+ else
41
+ db[uri] = result[2]
42
+ end
43
+ end
44
+ end
45
+ if [:create, :update, :delete].include? context.action
46
+ thing = context.forward(:readAll)
47
+ if thing[0] == 200
48
+ env.transaction do
49
+ db[all] = thing[2]
50
+ end
51
+ end
52
+ end
53
+ result
54
+ end
46
55
 
47
- def self.get_cached(context)
48
- uri = context.uri.join('/')
49
- env = $cache[context.app[:remote_uri]]
50
- db = env.database
51
- result = nil
52
- env.transaction do
53
- result = db[uri]
54
- end
55
- result
56
- end
56
+ def self.get_cached(context)
57
+ uri = context.uri.join('/')
58
+ env = $cache[context.config['remote']]
59
+ db = env.database
60
+ result = nil
61
+ env.transaction do
62
+ result = db[uri]
63
+ end
64
+ result
65
+ end
57
66
 
58
- def self.purge_cached(context)
59
- uri = context.uri.join('/')
60
- env = $cache[context.app[:remote_uri]]
61
- db = env.database
62
- result = 200
63
- env.transaction do
64
- begin
65
- db.delete(uri)
66
- rescue
67
- result = 404
68
- end
69
- end
70
- result
71
- end
67
+ def self.purge_cached(context)
68
+ uri = context.uri.join('/')
69
+ env = $cache[context.config['remote']]
70
+ db = env.database
71
+ result = 200
72
+ env.transaction do
73
+ begin
74
+ db.delete(uri)
75
+ rescue
76
+ result = 404
77
+ end
78
+ end
79
+ result
80
+ end
72
81
 
73
- def self.invoke(actions,context)
82
+ def self.invoke(actions, context)
74
83
 
75
- # Create Cache if not set up
76
- unless $cache[context.app[:remote_uri]]
77
- $cache[context.app[:remote_uri]] = LMDB.new("/tmp/cache/#{context.app[:remote_uri]}", mapsize: 2**30)
78
- end
84
+ # Create Cache if not set up
85
+ unless $cache[context.config['remote']]
86
+ $cache[context.config['remote']] = LMDB.new("/tmp/cache/#{context.config['remote']}", mapsize: 2**30)
87
+ end
79
88
 
80
- case context.action
81
- when :create,:update,:delete
82
- result = forward(context.action,context)
83
- update_cached(context) # write aware
84
- result
85
- when :read,:readAll
86
- cached = get_cached(context)
87
- unless cached
88
- cached = update_cached(context)
89
- end
90
- cached
91
- else
92
- 403
93
- end
94
- end
95
- end
89
+ case context.action
90
+ when :create, :update, :delete
91
+ result = context.forward(context.action)
92
+ update_cached(context) # write aware
93
+ result
94
+ when :read, :readAll
95
+ cached = get_cached(context)
96
+ unless cached
97
+ cached = update_cached(context)
98
+ end
99
+ cached
100
+ else
101
+ 403
102
+ end
103
+ end
104
+ end
96
105
  end
data/lib/app/db.rb CHANGED
@@ -1,209 +1,208 @@
1
- require 'data_objects'
2
- require 'dm-serializer/to_json'
3
- require_relative '../app'
4
- require_relative '../closet/resource'
1
+ ##
2
+ # Copyright 2017 Bryan T. Meyers
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ##
16
+
17
+ require 'base64'
18
+ require 'sequel'
5
19
 
6
20
  # DB is a Wire::App for generating REST wrappers for DataMapper
7
21
  # @author Bryan T. Meyers
8
22
  module DB
9
- include Wire::App
10
- include Wire::Resource
11
23
 
12
- # Setup a DB connection
13
- # @param [Symbol] namespace namespace used by DataMapper for Repositories
14
- # @param [String] location connection string (e.g. mysql://localhost/Foo)
15
- # @return [void]
16
- def self.db(namespace, location)
17
- $current_app[:db_namespace] = namespace
18
- $current_app[:db_location] = location
19
- DataMapper.setup(namespace, location)
20
- end
24
+ # DB-specific configuration
25
+ # @param [Hash] conf the existing configuration
26
+ # @return [Hash] post-processed configuration
27
+ def self.configure(conf)
28
+ Sequel.connect($environment['db'][conf['db']])
29
+ conf['models'].each do |m|
30
+ conf['models'][m] = Object.const_get(m)
31
+ end
32
+ conf
33
+ end
34
+
35
+ # Add a new object to the DB table
36
+ # @param [Hash] context the context for this request
37
+ # @return [Response] a valid Rack response triplet, or status code
38
+ def self.do_create(context)
39
+ model = context.config['models'][context.resource]
40
+ return 404 unless model
41
+
42
+ file = context.json[:file]
43
+ if file
44
+ if file[:mime].eql? 'text/csv'
45
+ file[:content].match(/.*base64,(.*)/) do
46
+ csv = Base64.decode64($1)
47
+ columns = []
48
+ errors = []
49
+ csv.split("\n").each_with_index do |v, i|
50
+ if i == 0
51
+ columns = v.split(/(?<!\[),(?!=\])/)
52
+ columns.map! { |c| c.delete('"').to_sym }
53
+ else
54
+ values = v.split(',')
55
+ values.map! do |c|
56
+ c.include?(';') ? c.split(';') : c
57
+ end
58
+ hash = {}
59
+ columns.each_with_index do |c, j|
60
+ if values[j].is_a? String
61
+ values[j].delete!('"')
62
+ end
63
+ hash[c] = values[j]
64
+ end
65
+ m = model.find_or_create(hash)
66
+ unless m.modified?
67
+ errors << "row: #{i} errors: #{m.errors.delete("\n")}"
68
+ end
69
+ end
70
+ end
71
+ if errors.length > 0
72
+ [400, nil, errors]
73
+ else
74
+ 200
75
+ end
76
+ end
77
+ else
78
+ 415
79
+ end
80
+ else
81
+ #TODO user_id needs to happen in the context
82
+ if model.respond_to? :updated_by_id
83
+ context.json[:updated_by_id] = context.user
84
+ end
85
+ if model.respond_to? :created_by_id
86
+ context.json[:created_by_id] = context.user
87
+ end
88
+ begin
89
+ instance = model.create(context.json)
90
+ instance.save
91
+ if instance.modified?
92
+ 200
93
+ else
94
+ errors = ''
95
+ instance.errors.each { |e| errors += "#{e.to_s}\n" }
96
+ [504, {}, errors]
97
+ end
98
+ rescue => e
99
+ [500, {}, e.message]
100
+ end
101
+ end
102
+ end
21
103
 
22
- # Map a DataMapper::Model to a sub-URI
23
- # @param [String] resource the sub-URI
24
- # @param [Class] model the DataMapper model
25
- # @return [void]
26
- def self.model(resource, model)
27
- $current_app[:resources][resource] = { model: model }
28
- end
104
+ # Get all objects from the DB table
105
+ # @param [Hash] context the context for this request
106
+ # @return [Response] all objects, or status code
107
+ def self.do_read_all(context)
108
+ return 404 unless context.resource
109
+ model = context.config['models'][context.resource]
110
+ if model
111
+ hash = '[ '
112
+ model.each do |e|
113
+ hash << (e.to_json)
114
+ hash << ','
115
+ end
116
+ hash = hash[0...-1]
117
+ hash << ']'
118
+ [200, {}, hash]
119
+ else
120
+ 404
121
+ end
122
+ end
29
123
 
30
- # Add a new object to the DB table
31
- # @param [Hash] context the context for this request
32
- # @return [Response] a valid Rack response triplet, or status code
33
- def self.do_create(context)
34
- return 404 unless context.resource
35
- model = context.resource[:model]
36
- if model
37
- file = context.json[:file]
38
- if file
39
- if file[:mime].eql? 'text/csv'
40
- file[:content].match(/.*base64,(.*)/) do
41
- csv = Base64.decode64($1)
42
- columns = []
43
- errors = []
44
- csv.split("\n").each_with_index do |v, i|
45
- if i == 0
46
- columns = v.split(/(?<!\[),(?!=\])/)
47
- columns.map! { |c| c.delete('"').to_sym }
48
- else
49
- values = v.split(',')
50
- values.map! do |c|
51
- c.include?(';') ? c.split(';') : c
52
- end
53
- hash = {}
54
- columns.each_with_index do |c, j|
55
- if values[j].is_a? String
56
- values[j].delete!('"')
57
- end
58
- hash[c] = values[j]
59
- end
60
- m = model.first_or_create(hash)
61
- unless m.saved?
62
- errors << "row: #{i} errors: #{m.errors.delete("\n")}"
63
- end
64
- end
65
- end
66
- if errors.length > 0
67
- [400, nil, errors]
68
- else
69
- 200
70
- end
71
- end
72
- else
73
- 415
74
- end
75
- else
76
- if model.instance_methods.include?(:updated_by)
77
- context.json[:updated_by] = context.user
78
- end
79
- if model.instance_methods.include?(:created_by)
80
- context.json[:created_by] = context.user
81
- end
82
- begin
83
- instance = model.create(context.json)
84
- instance.save
85
- if instance.saved?
86
- 200
87
- else
88
- errors = ''
89
- instance.errors.each { |e| errors += "#{e.to_s}\n"}
90
- [504,{}, errors]
91
- end
92
- rescue => e
93
- case e.class
94
- when DataObjects::IntegrityError.class
95
- [400,{},e.message]
96
- else
97
- [500,{},e.message]
98
- end
99
- end
100
- end
101
- else
102
- 404
103
- end
104
- end
124
+ # Get a specific object from the DB table
125
+ # @param [Hash] context the context for this request
126
+ # @return [Response] an object, or status code
127
+ def self.do_read(context)
128
+ model = context.config['models'][context.resource]
129
+ return 404 unless model
130
+ id = context.id
131
+ if id.eql?('new') or id.eql? 'upload'
132
+ return '{}'
133
+ end
134
+ if model
135
+ object = model[id]
136
+ if object
137
+ return [200, {}, object.to_json]
138
+ end
139
+ end
140
+ [404, {}, []]
141
+ end
105
142
 
106
- # Get all objects from the DB table
107
- # @param [Hash] context the context for this request
108
- # @return [Response] all objects, or status code
109
- def self.do_read_all(context)
110
- return 404 unless context.resource
111
- model = context.resource[:model]
112
- if model
113
- hash = '[ '
114
- model.all.each do |e|
115
- hash << (e.to_json)
116
- if e != model.all.last
117
- hash << ','
118
- end
119
- end
120
- hash << ']'
121
- [200,{},hash]
122
- else
123
- 404
124
- end
125
- end
143
+ # Update a specific object in the DB table
144
+ # @param [Hash] context the context for this request
145
+ # @return [Response] an object, or status code
146
+ def self.do_update(context)
147
+ model = context.config['models'][context.resource]
148
+ return 404 unless model
126
149
 
127
- # Get a specific object from the DB table
128
- # @param [Hash] context the context for this request
129
- # @return [Response] an object, or status code
130
- def self.do_read(context)
131
- return 404 unless context.resource
132
- model = context.resource[:model]
133
- id = context.uri[3]
134
- if id.eql?('new') or id.eql? 'upload'
135
- return '{}'
136
- end
137
- if model
138
- object = model.get(id)
139
- if object
140
- return [200,{},object.to_json]
141
- end
142
- end
143
- [404,{},[]]
144
- end
150
+ id = context.id
151
+ if model
152
+ if model.respond_to?(:updated_by_id)
153
+ context.json[:updated_by_id] = context.user
154
+ end
155
+ instance = model[id]
156
+ instance.update(context.json)
157
+ else
158
+ 404
159
+ end
160
+ end
145
161
 
146
- # Update a specific object in the DB table
147
- # @param [Hash] context the context for this request
148
- # @return [Response] an object, or status code
149
- def self.do_update(context)
150
- return 404 unless context.resource
151
- model = context.resource[:model]
152
- id = context.uri[3]
153
- if model
154
- if model.respond_to?(:updated_by)
155
- context.json[:updated_by] = context.user
156
- end
157
- instance = model.get(id)
158
- instance.update(context.json)
159
- else
160
- 404
161
- end
162
- end
162
+ # Remove a specific object from the DB table
163
+ # @param [Hash] context the context for this request
164
+ # @return [Response] an object, or status code
165
+ def self.do_delete(context)
166
+ model = context.config['models'][context.resource]
167
+ return 404 unless model
163
168
 
164
- # Remove a specific object from the DB table
165
- # @param [Hash] context the context for this request
166
- # @return [Response] an object, or status code
167
- def self.do_delete(context)
168
- return 404 unless context.resource
169
- model = context.resource[:model]
170
- id = context.uri[3]
171
- if model
172
- instance = model.get(id)
173
- if instance
174
- if instance.destroy
175
- 200
176
- else
177
- [500,{},'Failed to delete instance']
178
- end
179
- else
180
- 404
181
- end
182
- else
183
- 404
184
- end
185
- end
169
+ id = context.id
170
+ if model
171
+ instance = model[id]
172
+ if instance
173
+ if instance.destroy
174
+ 200
175
+ else
176
+ [500, {}, 'Failed to delete instance']
177
+ end
178
+ else
179
+ 404
180
+ end
181
+ else
182
+ 404
183
+ end
184
+ end
186
185
 
187
- # Proxy method used when routing
188
- # @param [Array] actions the allowed actions for this URI
189
- # @param [Hash] context the context for this request
190
- # @return [Response] a Rack Response triplet, or status code
191
- def self.invoke(actions, context)
192
- case context.action
193
- when :create
194
- do_create(context)
195
- when :read
196
- if context.uri[3]
197
- do_read(context)
198
- else
199
- do_read_all(context)
200
- end
201
- when :update
202
- do_update(context)
203
- when :delete
204
- do_delete(context)
205
- else
206
- 403
207
- end
208
- end
186
+ # Proxy method used when routing
187
+ # @param [Array] actions the allowed actions for this URI
188
+ # @param [Hash] context the context for this request
189
+ # @return [Response] a Rack Response triplet, or status code
190
+ def self.invoke(actions, context)
191
+ case context.action
192
+ when :create
193
+ do_create(context)
194
+ when :read
195
+ if context.uri[3]
196
+ do_read(context)
197
+ else
198
+ do_read_all(context)
199
+ end
200
+ when :update
201
+ do_update(context)
202
+ when :delete
203
+ do_delete(context)
204
+ else
205
+ 403
206
+ end
207
+ end
209
208
  end