shaf 0.1.0.beta

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