angus 0.0.1 → 0.0.2
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/bin/angus +13 -0
- data/lib/angus.rb +6 -4
- data/lib/angus/base.rb +119 -0
- data/lib/angus/base_actions.rb +38 -0
- data/lib/angus/base_resource.rb +14 -0
- data/lib/angus/command.rb +27 -0
- data/lib/angus/generator.rb +141 -0
- data/lib/angus/generator/templates/Gemfile +5 -0
- data/lib/angus/generator/templates/README.md +0 -0
- data/lib/angus/generator/templates/config.ru.erb +10 -0
- data/lib/angus/generator/templates/definitions/messages.yml +0 -0
- data/lib/angus/generator/templates/definitions/operations.yml.erb +21 -0
- data/lib/angus/generator/templates/definitions/representations.yml +0 -0
- data/lib/angus/generator/templates/definitions/service.yml.erb +3 -0
- data/lib/angus/generator/templates/resources/resource.rb.erb +6 -0
- data/lib/angus/generator/templates/services/service.rb.erb +4 -0
- data/lib/angus/marshallings/base.rb +2 -0
- data/lib/angus/marshallings/marshalling.rb +136 -0
- data/lib/angus/marshallings/unmarshalling.rb +29 -0
- data/lib/angus/renders/base.rb +2 -0
- data/lib/angus/renders/html_render.rb +9 -0
- data/lib/angus/renders/json_render.rb +15 -0
- data/lib/angus/request_handler.rb +62 -0
- data/lib/angus/resource_definition.rb +78 -0
- data/lib/angus/response.rb +53 -0
- data/lib/angus/responses.rb +232 -0
- data/lib/angus/rspec/spec_helper.rb +3 -0
- data/lib/angus/rspec/support/examples.rb +64 -0
- data/lib/angus/rspec/support/examples/describe_errors.rb +36 -0
- data/lib/angus/rspec/support/matchers/operation_response_matchers.rb +117 -0
- data/lib/angus/rspec/support/operation_response.rb +57 -0
- data/lib/angus/utils.rb +2 -0
- data/lib/angus/utils/params.rb +24 -0
- data/lib/angus/utils/string.rb +27 -0
- data/lib/angus/version.rb +2 -2
- data/spec/angus/rspec/examples/describe_errors_spec.rb +64 -0
- data/spec/spec_helper.rb +27 -0
- metadata +181 -19
- data/.gitignore +0 -17
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -22
- data/README.md +0 -29
- data/Rakefile +0 -1
- data/angus.gemspec +0 -23
data/bin/angus
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# resolve bin path, ignoring symlinks
|
5
|
+
require 'pathname'
|
6
|
+
bin_file = Pathname.new(__FILE__).realpath
|
7
|
+
|
8
|
+
# add self to libpath
|
9
|
+
$:.unshift File.expand_path('../../lib', bin_file)
|
10
|
+
|
11
|
+
require 'angus/command'
|
12
|
+
|
13
|
+
Angus::Command.start
|
data/lib/angus.rb
CHANGED
data/lib/angus/base.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require_relative 'resource_definition'
|
5
|
+
require_relative 'request_handler'
|
6
|
+
require_relative 'base_resource'
|
7
|
+
require_relative 'responses'
|
8
|
+
|
9
|
+
require_relative 'renders/base'
|
10
|
+
require_relative 'marshallings/base'
|
11
|
+
require_relative 'base_actions'
|
12
|
+
|
13
|
+
require 'angus/sdoc'
|
14
|
+
|
15
|
+
module Angus
|
16
|
+
class Base < RequestHandler
|
17
|
+
include BaseActions
|
18
|
+
|
19
|
+
FIRST_VERSION = '0.1'
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super
|
23
|
+
|
24
|
+
@resources_definitions = []
|
25
|
+
@version = FIRST_VERSION
|
26
|
+
@name = self.class.name.downcase
|
27
|
+
@configured = false
|
28
|
+
@definitions = nil
|
29
|
+
@logger = Logger.new(STDOUT)
|
30
|
+
|
31
|
+
configure!
|
32
|
+
|
33
|
+
register_base_routes
|
34
|
+
register_resources_routes
|
35
|
+
end
|
36
|
+
|
37
|
+
def register_resources_routes
|
38
|
+
@resources_definitions.each do |resource|
|
39
|
+
register_resource_routes(resource)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def configured?
|
44
|
+
@configured
|
45
|
+
end
|
46
|
+
|
47
|
+
# TODO ver que hacer
|
48
|
+
def configure
|
49
|
+
end
|
50
|
+
|
51
|
+
def configure!
|
52
|
+
raise 'Already configured' if configured?
|
53
|
+
|
54
|
+
# TODO ver como hacer configurable
|
55
|
+
@definitions = Angus::SDoc::DefinitionsReader.service_definition('definitions')
|
56
|
+
|
57
|
+
configure
|
58
|
+
|
59
|
+
@configured = true
|
60
|
+
end
|
61
|
+
|
62
|
+
def service_code_name
|
63
|
+
@definitions.code_name
|
64
|
+
end
|
65
|
+
|
66
|
+
def service_version
|
67
|
+
@definitions.version
|
68
|
+
end
|
69
|
+
|
70
|
+
def register(resource_name, options = {})
|
71
|
+
resource_definition = ResourceDefinition.new(resource_name, @definitions)
|
72
|
+
|
73
|
+
@resources_definitions << resource_definition
|
74
|
+
end
|
75
|
+
|
76
|
+
def base_path
|
77
|
+
"/#{service_code_name}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def register_resource_routes(resource_definition)
|
81
|
+
resource_definition.operations.each do |operation|
|
82
|
+
method = operation.method.to_sym
|
83
|
+
op_path = "#{api_path}#{operation.path}"
|
84
|
+
|
85
|
+
response_metadata = resource_definition.build_response_metadata(operation.response_elements)
|
86
|
+
|
87
|
+
router.on(method, op_path) do |env, params|
|
88
|
+
request = Rack::Request.new(env)
|
89
|
+
params = Params.indifferent_params(params)
|
90
|
+
|
91
|
+
resource = resource_definition.resource_class.new(request, params)
|
92
|
+
|
93
|
+
begin
|
94
|
+
response = resource.send(operation.code_name)
|
95
|
+
|
96
|
+
response = {} unless response.is_a?(Hash)
|
97
|
+
|
98
|
+
messages = response.delete(:messages)
|
99
|
+
|
100
|
+
response = build_data_response(response, response_metadata, messages)
|
101
|
+
|
102
|
+
@response.write(response)
|
103
|
+
rescue Exception => error
|
104
|
+
status_code = get_error_status_code(error)
|
105
|
+
if status_code == Angus::Responses::HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR
|
106
|
+
@logger.error("An exception occurs on #{resource.class.name}##{operation.code_name}")
|
107
|
+
@logger.error(error)
|
108
|
+
end
|
109
|
+
response = build_error_response(error)
|
110
|
+
|
111
|
+
@response.status = status_code
|
112
|
+
@response.write(response)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Angus
|
2
|
+
module BaseActions
|
3
|
+
|
4
|
+
def discover_paths
|
5
|
+
{
|
6
|
+
'doc' => doc_path,
|
7
|
+
'api' => api_path
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def register_base_routes
|
12
|
+
router.on(:get, '/') do
|
13
|
+
render discover_paths
|
14
|
+
end
|
15
|
+
|
16
|
+
router.on(:get, base_path) do
|
17
|
+
render discover_paths
|
18
|
+
end
|
19
|
+
|
20
|
+
router.on(:get, doc_path) do |env, params|
|
21
|
+
if params[:format] == 'json'
|
22
|
+
render(Angus::SDoc::JsonFormatter.format_service(@definitions), format: :json)
|
23
|
+
else
|
24
|
+
render(Angus::SDoc::HtmlFormatter.format_service(@definitions), format: :html)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def doc_path
|
30
|
+
"#{base_path}/doc/#{service_version}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def api_path
|
34
|
+
"#{base_path}/api/#{service_version}"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
require_relative 'generator'
|
5
|
+
|
6
|
+
module Angus
|
7
|
+
class Command < Thor
|
8
|
+
|
9
|
+
desc 'new [NAME]', 'Generate a new service'
|
10
|
+
def new(name)
|
11
|
+
generator.new_service(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'resource [NAME]', 'Generate a new resource'
|
15
|
+
method_option :actions, aliases: '-a', type: :array, desc: 'Generate the given actions for the resource'
|
16
|
+
def resource(name)
|
17
|
+
generator.resource(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def generator
|
23
|
+
@generator ||= Angus::Generator.new
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'thor'
|
3
|
+
require 'thor/actions'
|
4
|
+
|
5
|
+
module Angus
|
6
|
+
class Generator < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
RESOURCES_DIR = 'resources'
|
10
|
+
DEFINITIONS_DIR = 'definitions'
|
11
|
+
SERVICES_DIR = 'services'
|
12
|
+
|
13
|
+
NEW_APP_DIRECTORIES = %W(#{DEFINITIONS_DIR} #{RESOURCES_DIR} #{SERVICES_DIR})
|
14
|
+
|
15
|
+
NEW_APP_FILES = %w(
|
16
|
+
Gemfile
|
17
|
+
config.ru.erb
|
18
|
+
services/service.rb.erb
|
19
|
+
definitions/messages.yml
|
20
|
+
definitions/representations.yml
|
21
|
+
definitions/service.yml.erb
|
22
|
+
)
|
23
|
+
|
24
|
+
FILE_MAPPINGS = {
|
25
|
+
'services/service.rb.erb' => -> command, app_name { File.join(SERVICES_DIR, "#{command.underscore(command.classify(app_name))}.rb") },
|
26
|
+
'resources/resource.rb.erb' => -> command, name { "#{command.underscore(command.classify(name))}.rb" },
|
27
|
+
'definitions/operations.yml.erb' => -> command, _ { File.join(command.resource_name, 'operations.yml') }
|
28
|
+
}
|
29
|
+
|
30
|
+
no_commands do
|
31
|
+
def new_service(name)
|
32
|
+
app_name(name)
|
33
|
+
|
34
|
+
empty_directory(app_name)
|
35
|
+
|
36
|
+
NEW_APP_DIRECTORIES.each do |directory|
|
37
|
+
empty_directory(File.join(app_name, directory))
|
38
|
+
end
|
39
|
+
|
40
|
+
NEW_APP_FILES.each do |file|
|
41
|
+
if is_erb?(file)
|
42
|
+
copy_erb_file(file, app_name)
|
43
|
+
else
|
44
|
+
copy_file(file, File.join(app_name, file))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resource(name)
|
50
|
+
resource_name(name)
|
51
|
+
resource_actions(options[:actions])
|
52
|
+
|
53
|
+
empty_directory(RESOURCES_DIR)
|
54
|
+
empty_directory(DEFINITIONS_DIR)
|
55
|
+
|
56
|
+
copy_erb_file('resources/resource.rb.erb', resource_name, RESOURCES_DIR)
|
57
|
+
copy_erb_file('definitions/operations.yml.erb', resource_name, DEFINITIONS_DIR)
|
58
|
+
insert_into_services(" register :#{resource_name}\n")
|
59
|
+
end
|
60
|
+
|
61
|
+
def app_name(name = nil)
|
62
|
+
if name.nil?
|
63
|
+
@app_name
|
64
|
+
else
|
65
|
+
@app_name = name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def resource_name(name = nil)
|
70
|
+
if name.nil?
|
71
|
+
@resource_name
|
72
|
+
else
|
73
|
+
@resource_name = name
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def resource_actions(actions = nil)
|
78
|
+
if actions.nil?
|
79
|
+
@resource_actions || []
|
80
|
+
else
|
81
|
+
@resource_actions = actions
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def classify(string)
|
86
|
+
string.sub(/^[a-z\d]*/) { $&.capitalize }.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
|
87
|
+
end
|
88
|
+
|
89
|
+
def underscore(camel_cased_word)
|
90
|
+
word = camel_cased_word.to_s.dup
|
91
|
+
word.gsub!(/::/, '/')
|
92
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
93
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
94
|
+
word.tr!('-', '_')
|
95
|
+
word.downcase!
|
96
|
+
word
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def is_erb?(file)
|
103
|
+
file.end_with?('.erb')
|
104
|
+
end
|
105
|
+
|
106
|
+
def copy_erb_file(file, name, base_path = nil)
|
107
|
+
base_path = name if base_path.nil?
|
108
|
+
|
109
|
+
tmp_file = Tempfile.new(File.basename(file))
|
110
|
+
|
111
|
+
source = File.expand_path(find_in_source_paths(file.to_s))
|
112
|
+
content = ERB.new(File.binread(source)).result(binding)
|
113
|
+
|
114
|
+
File.open(tmp_file.path, 'w') { |f| f << content }
|
115
|
+
tmp_file.close
|
116
|
+
|
117
|
+
copy_file(tmp_file.path, File.join(base_path, filename_resolver(file, name)))
|
118
|
+
end
|
119
|
+
|
120
|
+
def insert_into_services(content)
|
121
|
+
config = {}
|
122
|
+
config.merge!(:after => /def configure\n|def configure .*\n/)
|
123
|
+
|
124
|
+
file = Dir[File.join(Dir.pwd, SERVICES_DIR, '*.*')].first
|
125
|
+
|
126
|
+
insert_into_file(file, *([content] << config))
|
127
|
+
end
|
128
|
+
|
129
|
+
def filename_resolver(file, app_name)
|
130
|
+
if FILE_MAPPINGS[file].nil?
|
131
|
+
file.gsub('.erb', '')
|
132
|
+
else
|
133
|
+
FILE_MAPPINGS[file].call(self, app_name)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
Angus::Generator.source_root(File.join(File.dirname(File.expand_path(__FILE__)), 'generator',
|
141
|
+
'templates'))
|
File without changes
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% resource_actions.each do |action_name| %>
|
2
|
+
<%= action_name%>:
|
3
|
+
name:
|
4
|
+
description: |
|
5
|
+
|
6
|
+
path:
|
7
|
+
method:
|
8
|
+
uri:
|
9
|
+
- element:
|
10
|
+
description:
|
11
|
+
|
12
|
+
response:
|
13
|
+
- element:
|
14
|
+
description:
|
15
|
+
required:
|
16
|
+
type:
|
17
|
+
|
18
|
+
messages:
|
19
|
+
- key:
|
20
|
+
description:
|
21
|
+
<% end %>
|
File without changes
|