shaf 0.1.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -0
  4. data/bin/shaf +57 -0
  5. data/lib/shaf.rb +9 -0
  6. data/lib/shaf/api_doc.rb +124 -0
  7. data/lib/shaf/api_doc/comment.rb +27 -0
  8. data/lib/shaf/api_doc/document.rb +133 -0
  9. data/lib/shaf/app.rb +22 -0
  10. data/lib/shaf/command.rb +42 -0
  11. data/lib/shaf/command/console.rb +17 -0
  12. data/lib/shaf/command/generate.rb +19 -0
  13. data/lib/shaf/command/new.rb +79 -0
  14. data/lib/shaf/command/server.rb +15 -0
  15. data/lib/shaf/command/templates/Gemfile.erb +30 -0
  16. data/lib/shaf/doc_model.rb +54 -0
  17. data/lib/shaf/errors.rb +77 -0
  18. data/lib/shaf/extensions.rb +11 -0
  19. data/lib/shaf/extensions/authorize.rb +42 -0
  20. data/lib/shaf/extensions/resource_uris.rb +153 -0
  21. data/lib/shaf/formable.rb +188 -0
  22. data/lib/shaf/generator.rb +69 -0
  23. data/lib/shaf/generator/controller.rb +106 -0
  24. data/lib/shaf/generator/migration.rb +122 -0
  25. data/lib/shaf/generator/migration/add_column.rb +49 -0
  26. data/lib/shaf/generator/migration/create_table.rb +40 -0
  27. data/lib/shaf/generator/migration/drop_column.rb +45 -0
  28. data/lib/shaf/generator/migration/empty.rb +21 -0
  29. data/lib/shaf/generator/migration/rename_column.rb +48 -0
  30. data/lib/shaf/generator/model.rb +68 -0
  31. data/lib/shaf/generator/policy.rb +43 -0
  32. data/lib/shaf/generator/scaffold.rb +26 -0
  33. data/lib/shaf/generator/serializer.rb +258 -0
  34. data/lib/shaf/generator/templates/api/controller.rb.erb +62 -0
  35. data/lib/shaf/generator/templates/api/model.rb.erb +20 -0
  36. data/lib/shaf/generator/templates/api/policy.rb.erb +26 -0
  37. data/lib/shaf/generator/templates/api/serializer.rb.erb +24 -0
  38. data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +98 -0
  39. data/lib/shaf/generator/templates/spec/model.rb.erb +40 -0
  40. data/lib/shaf/generator/templates/spec/serializer_spec.rb.erb +46 -0
  41. data/lib/shaf/helpers.rb +15 -0
  42. data/lib/shaf/helpers/json_html.rb +65 -0
  43. data/lib/shaf/helpers/paginate.rb +24 -0
  44. data/lib/shaf/helpers/payload.rb +115 -0
  45. data/lib/shaf/helpers/session.rb +53 -0
  46. data/lib/shaf/middleware.rb +1 -0
  47. data/lib/shaf/middleware/request_id.rb +16 -0
  48. data/lib/shaf/registrable_factory.rb +71 -0
  49. data/lib/shaf/settings.rb +33 -0
  50. data/lib/shaf/spec.rb +6 -0
  51. data/lib/shaf/spec/http_method_utils.rb +24 -0
  52. data/lib/shaf/spec/integration_spec.rb +53 -0
  53. data/lib/shaf/spec/model.rb +17 -0
  54. data/lib/shaf/spec/payload_test.rb +78 -0
  55. data/lib/shaf/spec/payload_utils.rb +176 -0
  56. data/lib/shaf/spec/serializer_spec.rb +24 -0
  57. data/lib/shaf/tasks.rb +4 -0
  58. data/lib/shaf/tasks/db.rb +61 -0
  59. data/lib/shaf/tasks/test.rb +43 -0
  60. data/lib/shaf/utils.rb +53 -0
  61. data/lib/shaf/version.rb +3 -0
  62. data/templates/Rakefile +13 -0
  63. data/templates/api/controllers/base_controller.rb +57 -0
  64. data/templates/api/controllers/docs_controller.rb +16 -0
  65. data/templates/api/controllers/root_controller.rb +8 -0
  66. data/templates/api/serializers/error_serializer.rb +10 -0
  67. data/templates/api/serializers/form_serializer.rb +42 -0
  68. data/templates/api/serializers/root_serializer.rb +16 -0
  69. data/templates/config.ru +4 -0
  70. data/templates/config/bootstrap.rb +12 -0
  71. data/templates/config/constants.rb +5 -0
  72. data/templates/config/customize.rb +3 -0
  73. data/templates/config/database.rb +40 -0
  74. data/templates/config/directories.rb +32 -0
  75. data/templates/config/helpers.rb +18 -0
  76. data/templates/config/initializers.rb +12 -0
  77. data/templates/config/initializers/db_migrations.rb +18 -0
  78. data/templates/config/initializers/hal_presenter.rb +6 -0
  79. data/templates/config/initializers/logging.rb +7 -0
  80. data/templates/config/initializers/sequel.rb +4 -0
  81. data/templates/config/settings.yml +19 -0
  82. data/templates/frontend/assets/css/main.css +70 -0
  83. data/templates/frontend/views/form.erb +16 -0
  84. data/templates/frontend/views/layout.erb +11 -0
  85. data/templates/frontend/views/payload.erb +8 -0
  86. data/templates/spec/integration/root_spec.rb +14 -0
  87. data/templates/spec/serializers/root_serializer_spec.rb +12 -0
  88. data/templates/spec/spec_helper.rb +4 -0
  89. metadata +348 -0
  90. metadata.gz.sig +0 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f841c557430b49c07e938734ae86d33572a7d6fa2b7ca5a65ae3c85d0a88cd65
4
+ data.tar.gz: b01be584adcba338f10942a79b80a2f55625383e880b119de5c5f9a53212e196
5
+ SHA512:
6
+ metadata.gz: 4e9006af2484b780d9d41781d8060c58658c6c398152353e475d8f4c8832fc8397589d1798d9e805faa71bf8de537c63453493c8c67cae74190261b1a0236294
7
+ data.tar.gz: 2f62a554a41c79cd42a2669e6c844ece355db39a829754abe5dec7d81055949d88be6954ee4874b261181060374a06310b63790e7ed944edd521f8def10af942
Binary file
@@ -0,0 +1 @@
1
+ �q''�@e-<���Í�[%݁�cHݖ��p�[ pj�u��[^����M@K7�!���v������V-u�J��ה}/��I8�:�Ti���g����ӥ��]R�6*��5"�机�-����3�Ex=�.mhHc�iF��]��7=�T��0},�$4Ϙ�O՝���*@X�s�/�es�f�����G7��;��"�}��4,Qso^�"#@]��Ч6��D��C��T�!��x������
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'shaf/utils'
4
+ require 'shaf/command'
5
+ require 'shaf/settings'
6
+
7
+ module Shaf
8
+ class Script
9
+ include Utils
10
+
11
+ def self.run
12
+ new.run
13
+ end
14
+
15
+ def run
16
+ check_customizations
17
+ return show_help if show_help?
18
+ Command::Factory.create(*ARGV).call
19
+ rescue RegistrableFactory::NotFoundError, Command::ArgumentError => e
20
+ puts e.message, "\n"
21
+ show_help
22
+ exit 1
23
+ rescue Utils::ProjectRootNotFound
24
+ puts "This command can only be executed inside a Shaf project directory. " \
25
+ "Please change directory and try again!"
26
+ exit 2
27
+ end
28
+
29
+ def show_help
30
+ puts "Usage: #{script_name} #{usage}"
31
+ end
32
+
33
+ def script_name
34
+ File.basename $0
35
+ end
36
+
37
+ def usage
38
+ Command::Factory.usage.join("\n ")
39
+ end
40
+
41
+ def show_help?
42
+ ARGV.first =~ /-h/
43
+ end
44
+
45
+ def check_customizations
46
+ return unless project_root
47
+
48
+ in_project_root do
49
+ ENV['RACK_ENV'] ||= 'development'
50
+ next unless File.exist? 'config/customize.rb'
51
+ require 'config/customize'
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ Shaf::Script.run
@@ -0,0 +1,9 @@
1
+ require 'shaf/version'
2
+ require 'shaf/settings'
3
+ require 'shaf/command'
4
+ require 'shaf/app'
5
+ require 'shaf/errors'
6
+ require 'shaf/formable'
7
+ require 'shaf/extensions'
8
+ require 'shaf/helpers'
9
+ require 'shaf/doc_model'
@@ -0,0 +1,124 @@
1
+ require 'shaf/api_doc/document'
2
+
3
+ module Shaf
4
+ module ApiDoc
5
+ class Task
6
+ include Rake::DSL
7
+
8
+ attr_accessor :document_class, :source_dir, :html_output_dir, :yaml_output_dir
9
+
10
+ def initialize
11
+ yield self if block_given?
12
+ validate_attributes!
13
+ @document_class ||= Document
14
+ define_tasks
15
+ end
16
+
17
+ def validate_attributes!
18
+ raise "source_dir must be set!" unless source_dir
19
+ raise "html_output_dir must be configured in ApiDocTask" unless html_output_dir
20
+ raise "yaml_output_dir must be configured in ApiDocTask" unless yaml_output_dir
21
+ end
22
+
23
+ def define_tasks
24
+ namespace :doc do
25
+ desc "Generate API documentation"
26
+ task :generate do
27
+ files = Dir.glob(File.join(source_dir, "*.rb"))
28
+ files.each do |file|
29
+ read_file file do |doc|
30
+ next unless doc.has_enough_info?
31
+ doc.write_html @html_output_dir
32
+ doc.write_yaml @yaml_output_dir
33
+ end
34
+ end
35
+ end
36
+
37
+ desc "Remove generated documentation"
38
+ task :clean do
39
+ [
40
+ Dir.glob(File.join(@yaml_output_dir, "*.yml")),
41
+ Dir.glob(File.join(@html_output_dir, "*.html"))
42
+ ].flatten.each do |file|
43
+ File.unlink file
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def read_file(file)
50
+ doc = document_class.new
51
+ comment = Comment.new
52
+
53
+ File.readlines(file).each do |line|
54
+ next if empty_line?(line)
55
+
56
+ if c = comment(line)
57
+ comment << c
58
+ next
59
+ end
60
+
61
+ parse_line(line, doc, comment)
62
+ comment = Comment.new
63
+ end
64
+
65
+ return doc unless block_given?
66
+ yield doc
67
+ end
68
+
69
+ def parse_line(line, doc, comment)
70
+ if model = model(line)
71
+ doc.model = model
72
+ elsif serializer_class = serializer_class(line)
73
+ doc.serializer_class = serializer_class
74
+ elsif policy = policy(line)
75
+ doc.policy = policy
76
+ elsif attr = attribute(line)
77
+ doc.attribute(attr, comment)
78
+ elsif rel = link(line)
79
+ doc.link(rel, comment)
80
+ elsif rel = curie(line)
81
+ doc.curie(rel, comment)
82
+ elsif name = embed(line)
83
+ doc.embedded(name, comment)
84
+ end
85
+ end
86
+
87
+ def empty_line?(line)
88
+ true if line[/\A[#\s*]*\Z/]
89
+ end
90
+
91
+ def serializer_class(line)
92
+ line[/\A\s*class\s*(\w+)\Z/, 1]
93
+ end
94
+
95
+ def model(line)
96
+ line[/\A\s*model\s*(?:::)?(\w+)/, 1]
97
+ end
98
+
99
+ def policy(line)
100
+ line[/\A\s*policy\s*(?:::)?(\w+)/, 1]
101
+ end
102
+
103
+ def comment(line)
104
+ line[/\A\s*#(.*)/, 1]
105
+ end
106
+
107
+ def attribute(line)
108
+ line[/\A\s*attribute[^s]\s*\(?\s*:(\w+)/, 1]
109
+ end
110
+
111
+ def link(line)
112
+ line[/\A\s*link\s*\(?\s*:'?([-:\w]+)'?/, 1]
113
+ end
114
+
115
+ def curie(line)
116
+ line[/\A\s*curie\s*\(?\s*:'?([-\w]+)'?/, 1]
117
+ end
118
+
119
+ def embed(line)
120
+ line[/\A\s*embed\s*\(?\s*:'?([-:\w]+)'?/, 1]
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,27 @@
1
+ module Shaf
2
+ module ApiDoc
3
+ class Comment
4
+ def initialize
5
+ @indent = 0
6
+ @comment = ""
7
+ end
8
+
9
+ def to_s
10
+ @comment
11
+ end
12
+
13
+ def empty?
14
+ @comment.empty?
15
+ end
16
+
17
+ def <<(line)
18
+ @indent = line[/\A\s*/].size if empty?
19
+ @comment << "\n#{extract(line)}"
20
+ end
21
+
22
+ def extract(line)
23
+ line.sub(%r(\A\s{#{@indent}}), "")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,133 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+ require 'redcarpet'
4
+ require 'redcarpet/render_strip'
5
+ require 'shaf/api_doc/comment'
6
+
7
+ module Shaf
8
+ module ApiDoc
9
+ class Document
10
+ attr_writer :model
11
+ attr_accessor :serializer_class, :policy, :attributes, :links, :curies, :embeds
12
+
13
+ def initialize
14
+ @model = nil
15
+ @serializer_class = nil
16
+ @policy = nil
17
+ @attributes = {}
18
+ @links = {}
19
+ @curies = {}
20
+ @embeds = {}
21
+ @md = {}
22
+ end
23
+
24
+ def model
25
+ @model || @serializer_class && @serializer_class.sub("Serializer", "")
26
+ end
27
+
28
+ def attribute(attr, comment)
29
+ @attributes[attr] = comment unless comment.empty?
30
+ end
31
+
32
+ def link(rel, comment)
33
+ @links[strip_curie(rel)] = comment unless comment.empty?
34
+ end
35
+
36
+ def curie(rel, comment)
37
+ @curies[rel] = comment unless comment.empty?
38
+ end
39
+
40
+ def embedded(name, comment)
41
+ @embeds[strip_curie(name)] = comment unless comment.empty?
42
+ end
43
+
44
+ def has_enough_info?
45
+ return false unless model
46
+ attributes.merge(links).merge(curies).any?
47
+ end
48
+
49
+ def generate_markdown!
50
+ return @md unless @md.empty?
51
+
52
+ generate_title!
53
+ generate_policy!
54
+ generate_section!(key: :attributes, heading: "Attributes")
55
+ generate_section!(key: :curies, heading: "Curies", sub_title: "rel")
56
+ generate_section!(key: :links, heading: "Links", sub_title: "rel")
57
+ generate_section!(key: :embeds, heading: "Embedded resources", sub_title: "rel")
58
+ @md[:doc]
59
+ end
60
+
61
+ def generate_yaml!
62
+ generate_markdown!
63
+ renderer = Redcarpet::Markdown.new(Redcarpet::Render::StripDown)
64
+
65
+ hash = {}
66
+ hash['policy'] = renderer.render(@md[:policy]).chomp if @md[:policy]
67
+
68
+ [:attributes, :curies, :links, :embeds].each do |key|
69
+ hash[key.to_s] = @md[key].map { |k, v| [k.to_s, renderer.render(v).chomp] }.to_h
70
+ end
71
+ hash.to_yaml
72
+ end
73
+
74
+ def to_markdown
75
+ # For some reason redcarpet don't like to surround my markdown code blocks
76
+ # with <pre> tags, so let's fix that here.
77
+ options = {autolink: true, fenced_code_blocks: true}
78
+ markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
79
+ html = markdown.render(generate_markdown!)
80
+ html.gsub!("<code>", "<pre><code>")
81
+ html.gsub!("</code>", "</code></pre>")
82
+ html
83
+ end
84
+
85
+ def write_html(output)
86
+ FileUtils.mkdir_p(output) unless Dir.exist? output
87
+ File.open(File.join(output, "#{model.downcase}.html"), "w") do |file|
88
+ file.write(to_markdown)
89
+ end
90
+ end
91
+
92
+ def write_yaml(output)
93
+ FileUtils.mkdir_p(output) unless Dir.exist? output
94
+ File.open(File.join(output, "#{model.downcase}.yml"), "w") do |file|
95
+ file.write(generate_yaml!)
96
+ end
97
+ end
98
+
99
+
100
+ private
101
+
102
+ def strip_curie(rel)
103
+ rel.split(':', 2)[1] || rel
104
+ end
105
+
106
+ def generate_title!
107
+ @md[:doc] = "##%s\n" % model.capitalize
108
+ @md[:title] = model.capitalize
109
+ end
110
+
111
+ def generate_policy!
112
+ return if policy.nil?
113
+ @md[:doc] << "###Policy\n#{policy}\n"
114
+ @md[:policy] = policy
115
+ end
116
+
117
+ def generate_section!(key:, heading:, sub_title: "")
118
+ list = send(key)
119
+ @md[:doc] << "####{heading}\n"
120
+ @md[key] = {}
121
+ if list.empty?
122
+ @md[:doc] << "This resource does not have any documented #{heading.downcase}\n"
123
+ else
124
+ sub_title << ": " unless sub_title.empty?
125
+ list.each do |name, comment|
126
+ @md[:doc] << "#######{sub_title}#{name.gsub('_', '-')}\n#{comment.to_s}\n"
127
+ @md[key][name] = comment.to_s.chomp
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,22 @@
1
+ require 'shaf/middleware'
2
+
3
+ module Shaf
4
+ class App
5
+ class << self
6
+ def instance
7
+ create_instance unless defined?(@instance)
8
+ @instance
9
+ end
10
+
11
+ def create_instance
12
+ @instance = Sinatra.new
13
+ @instance.set :port, Shaf::Settings.port || 3000
14
+ @instance.use Shaf::Middleware::RequestId
15
+ end
16
+
17
+ def use(middleware)
18
+ instance.use middleware
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ require 'shaf/utils'
2
+ require 'shaf/registrable_factory'
3
+
4
+ module Shaf
5
+ module Command
6
+
7
+ class ArgumentError < StandardError; end
8
+
9
+ class Factory
10
+ extend RegistrableFactory
11
+ end
12
+
13
+ class Base
14
+ include Utils
15
+
16
+ attr_reader :args
17
+
18
+ class << self
19
+ def inherited(child)
20
+ Factory.register(child)
21
+ end
22
+
23
+ def identifier(*ids)
24
+ @identifiers = ids.flatten
25
+ end
26
+
27
+ def usage(str = nil, &block)
28
+ @usage = str || block
29
+ end
30
+ end
31
+
32
+ def initialize(*args)
33
+ @args = args.dup
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ require 'shaf/command/new'
40
+ require 'shaf/command/server'
41
+ require 'shaf/command/console'
42
+ require 'shaf/command/generate'
@@ -0,0 +1,17 @@
1
+ require 'irb'
2
+
3
+ module Shaf
4
+ module Command
5
+ class Console < Base
6
+
7
+ identifier %r(\Ac(onsole)?\Z)
8
+ usage 'console'
9
+
10
+ def call
11
+ bootstrap
12
+ ARGV.clear
13
+ IRB.start
14
+ end
15
+ end
16
+ end
17
+ end