rodakase 0.0.1

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 (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +29 -0
  5. data/CHANGELOG.md +8 -0
  6. data/CODE_OF_CONDUCT.md +13 -0
  7. data/Gemfile +24 -0
  8. data/LICENSE +22 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +114 -0
  11. data/Rakefile +4 -0
  12. data/benchmarks/templates/button.erb +1 -0
  13. data/benchmarks/view.rb +27 -0
  14. data/lib/roda/plugins/flow.rb +71 -0
  15. data/lib/rodakase.rb +8 -0
  16. data/lib/rodakase/application.rb +32 -0
  17. data/lib/rodakase/cli.rb +9 -0
  18. data/lib/rodakase/component.rb +40 -0
  19. data/lib/rodakase/config.rb +21 -0
  20. data/lib/rodakase/container.rb +127 -0
  21. data/lib/rodakase/transaction.rb +43 -0
  22. data/lib/rodakase/transaction/matcher.rb +31 -0
  23. data/lib/rodakase/transaction/result.rb +52 -0
  24. data/lib/rodakase/version.rb +3 -0
  25. data/lib/rodakase/view.rb +2 -0
  26. data/lib/rodakase/view/layout.rb +98 -0
  27. data/lib/rodakase/view/part.rb +53 -0
  28. data/lib/rodakase/view/renderer.rb +67 -0
  29. data/rodakase.gemspec +34 -0
  30. data/skeletons/simple/Gemfile +9 -0
  31. data/skeletons/simple/Rakefile +4 -0
  32. data/skeletons/simple/bin/console +6 -0
  33. data/skeletons/simple/config.ru +3 -0
  34. data/skeletons/simple/config/application.yml +4 -0
  35. data/skeletons/simple/core/boot.rb +12 -0
  36. data/skeletons/simple/core/simple/application.rb +16 -0
  37. data/skeletons/simple/core/simple/container.rb +11 -0
  38. data/skeletons/simple/core/simple/import.rb +9 -0
  39. data/skeletons/simple/lib/entities/user.rb +3 -0
  40. data/skeletons/simple/log/.gitkeep +0 -0
  41. data/skeletons/simple/spec/routes/heartbeat_spec.rb +9 -0
  42. data/skeletons/simple/spec/spec_helper.rb +12 -0
  43. data/skeletons/simple/spec/unit/user_spec.rb +17 -0
  44. data/skeletons/simple/spec/web_helper.rb +20 -0
  45. data/skeletons/simple/web/routes/root.rb +3 -0
  46. data/spec/dummy/apps/main/core/boot.rb +9 -0
  47. data/spec/dummy/apps/main/core/main/application.rb +17 -0
  48. data/spec/dummy/apps/main/core/main/container.rb +12 -0
  49. data/spec/dummy/apps/main/core/main/import.rb +9 -0
  50. data/spec/dummy/apps/main/core/main/requests.rb +21 -0
  51. data/spec/dummy/apps/main/core/main/view.rb +19 -0
  52. data/spec/dummy/apps/main/lib/entities/user.rb +3 -0
  53. data/spec/dummy/apps/main/lib/persistence/repositories/users.rb +14 -0
  54. data/spec/dummy/apps/main/lib/transaction.rb +11 -0
  55. data/spec/dummy/apps/main/lib/transactions/register_user.rb +17 -0
  56. data/spec/dummy/apps/main/lib/views/users/index.rb +15 -0
  57. data/spec/dummy/apps/main/requests/users.rb +5 -0
  58. data/spec/dummy/apps/main/web/routes/users.rb +21 -0
  59. data/spec/dummy/apps/main/web/templates/layouts/app.slim +6 -0
  60. data/spec/dummy/apps/main/web/templates/users/index.slim +3 -0
  61. data/spec/dummy/apps/main/web/templates/users/index/_list.slim +3 -0
  62. data/spec/dummy/apps/main/web/templates/users/index/_list_item.slim +1 -0
  63. data/spec/dummy/bin/console +6 -0
  64. data/spec/dummy/config.ru +3 -0
  65. data/spec/dummy/config/application.yml +4 -0
  66. data/spec/dummy/core/boot.rb +15 -0
  67. data/spec/dummy/core/dummy/container.rb +14 -0
  68. data/spec/dummy/core/dummy/import.rb +9 -0
  69. data/spec/dummy/lib/persistence/db.rb +13 -0
  70. data/spec/dummy/log/.gitkeep +0 -0
  71. data/spec/fixtures/templates/hello.slim +1 -0
  72. data/spec/fixtures/templates/layouts/app.slim +6 -0
  73. data/spec/fixtures/templates/shared/_index_table.slim +2 -0
  74. data/spec/fixtures/templates/shared/_shared_hello.slim +1 -0
  75. data/spec/fixtures/templates/tasks.slim +3 -0
  76. data/spec/fixtures/templates/user.slim +2 -0
  77. data/spec/fixtures/templates/users.slim +3 -0
  78. data/spec/fixtures/templates/users/_row.slim +2 -0
  79. data/spec/fixtures/templates/users/_tbody.slim +5 -0
  80. data/spec/fixtures/test/core/boot/bar.rb +5 -0
  81. data/spec/fixtures/test/lib/test/dep.rb +4 -0
  82. data/spec/fixtures/test/lib/test/foo.rb +5 -0
  83. data/spec/fixtures/test/lib/test/models.rb +4 -0
  84. data/spec/fixtures/test/lib/test/models/book.rb +6 -0
  85. data/spec/fixtures/test/lib/test/models/user.rb +6 -0
  86. data/spec/integration/application_spec.rb +21 -0
  87. data/spec/integration/transaction_spec.rb +85 -0
  88. data/spec/integration/view_spec.rb +65 -0
  89. data/spec/request/users_spec.rb +35 -0
  90. data/spec/spec_helper.rb +44 -0
  91. data/spec/support/helpers.rb +15 -0
  92. data/spec/unit/component_spec.rb +60 -0
  93. data/spec/unit/container_spec.rb +51 -0
  94. data/spec/unit/view/layout_spec.rb +49 -0
  95. data/spec/unit/view/part_spec.rb +55 -0
  96. data/spec/unit/view/renderer_spec.rb +29 -0
  97. metadata +359 -0
@@ -0,0 +1,67 @@
1
+ require 'tilt'
2
+ require 'dry-equalizer'
3
+
4
+ module Rodakase
5
+ module View
6
+ class Renderer
7
+ include Dry::Equalizer(:dir, :root, :engine)
8
+
9
+ TemplateNotFoundError = Class.new(StandardError)
10
+
11
+ attr_reader :dir, :root, :engine, :tilts
12
+
13
+ def self.tilts
14
+ @__engines__ ||= {}
15
+ end
16
+
17
+ def initialize(dir, options = {})
18
+ @dir = dir
19
+ @root = options.fetch(:root, dir)
20
+ @engine = options[:engine]
21
+ @tilts = self.class.tilts
22
+ end
23
+
24
+ def call(template, scope, &block)
25
+ path = lookup(template)
26
+
27
+ if path
28
+ render(path, scope, &block)
29
+ else
30
+ raise TemplateNotFoundError, "Template #{template} could not be looked up within #{root}"
31
+ end
32
+ end
33
+
34
+ def render(path, scope, &block)
35
+ tilt(path).render(scope, &block)
36
+ end
37
+
38
+ def tilt(path)
39
+ tilts.fetch(path) { tilts[path] = Tilt[engine].new(path) }
40
+ end
41
+
42
+ def lookup(name)
43
+ template?(name) || template?("shared/#{name}") || !root? && chdir('..').lookup(name)
44
+ end
45
+
46
+ def root?
47
+ dir == root
48
+ end
49
+
50
+ def template?(name)
51
+ template_path = path(name)
52
+
53
+ if File.exist?(template_path)
54
+ template_path
55
+ end
56
+ end
57
+
58
+ def path(name)
59
+ dir.join("#{name}.#{engine}")
60
+ end
61
+
62
+ def chdir(dirname)
63
+ self.class.new(dir.join(dirname), engine: engine, root: root)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rodakase/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rodakase"
8
+ spec.version = Rodakase::VERSION
9
+ spec.authors = ["Piotr Solnica"]
10
+ spec.email = ["piotr.solnica@gmail.com"]
11
+ spec.summary = "Lightweight web application stack on top of Roda"
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/solnic/rodakase"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "inflecto", "~> 0"
22
+ spec.add_runtime_dependency "roda", "~> 2.7"
23
+ spec.add_runtime_dependency "tilt", "~> 2.0"
24
+ spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
25
+ spec.add_runtime_dependency "dry-container", "~> 0.2"
26
+ spec.add_runtime_dependency "dry-configurable", "~> 0.1"
27
+ spec.add_runtime_dependency "dry-auto_inject", "~> 0.1"
28
+ spec.add_runtime_dependency "transflow", "~> 0.3"
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.7"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.1"
33
+ spec.add_development_dependency "capybara", "~> 2.5"
34
+ end
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rodakase'
4
+ gem 'pry'
5
+
6
+ group :test do
7
+ gem 'rack-test'
8
+ gem 'rspec'
9
+ end
@@ -0,0 +1,4 @@
1
+ require "rspec/core/rake_task"
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
4
+ task default: [:spec]
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rodakase/cli'
4
+ require_relative '../core/boot'
5
+
6
+ Rodakase::Cli.start
@@ -0,0 +1,3 @@
1
+ require_relative 'core/boot'
2
+
3
+ run Simple::Application.freeze.app
@@ -0,0 +1,4 @@
1
+ development:
2
+ SOMETHING: 'goes-here'
3
+ test:
4
+ SOMETHING: 'goes-here'
@@ -0,0 +1,12 @@
1
+ require_relative 'simple/container'
2
+
3
+ Simple::Container.finalize! do |container|
4
+ # Register your additional dependencies used by the web app
5
+ #
6
+ # In example a logger:
7
+ #
8
+ # require 'logger'
9
+ # container.register(:logger, Logger.new(container.root.join("log/#{container.config.env}.log")))
10
+ end
11
+
12
+ require 'simple/application'
@@ -0,0 +1,16 @@
1
+ require 'rodakase/application'
2
+
3
+ module Simple
4
+ class Application < Rodakase::Application
5
+ configure do |config|
6
+ config.routes = 'web/routes'.freeze
7
+ config.container = Container
8
+ end
9
+
10
+ route do |r|
11
+ r.multi_route
12
+ end
13
+
14
+ load_routes!
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ require 'rodakase/container'
2
+
3
+ module Simple
4
+ class Container < Rodakase::Container
5
+ setting :auto_register, 'lib'
6
+
7
+ configure do
8
+ load_paths!('lib')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'container'
2
+
3
+ module Simple
4
+ Import = Container.import_module
5
+
6
+ def self.Import(*args)
7
+ Import[*args]
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Entities
2
+ User = Struct.new(:id, :name)
3
+ end
File without changes
@@ -0,0 +1,9 @@
1
+ require 'web_helper'
2
+
3
+ RSpec.describe '/hearbeat', type: :request do
4
+ it 'works' do
5
+ get '/heartbeat'
6
+
7
+ expect(last_response).to be_ok
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ # set the env here since the app config loaded by container uses this name by
2
+ # default
3
+ ENV['RACK_ENV'] = 'test'
4
+
5
+ # Only load the container, if a test needs the whole web stack it should require
6
+ # web_helper instead
7
+ require_relative '../core/simple/container'
8
+
9
+ RSpec.configure do |config|
10
+ # Obviously
11
+ config.disable_monkey_patching!
12
+ end
@@ -0,0 +1,17 @@
1
+ require 'entities/user'
2
+
3
+ RSpec.describe Entities::User do
4
+ subject(:user) { Entities::User.new(1, 'Jane') }
5
+
6
+ describe '#id' do
7
+ it 'works' do
8
+ expect(user.id).to be(1)
9
+ end
10
+ end
11
+
12
+ describe '#name' do
13
+ it 'works too' do
14
+ expect(user.name).to eql('Jane')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ require 'rack/test'
4
+
5
+ require_relative '../core/boot'
6
+
7
+ module AppHelper
8
+ def app
9
+ Simple::Application.app
10
+ end
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.before(:suite) do
15
+ Simple::Application.freeze
16
+ end
17
+
18
+ config.include Rack::Test::Methods, type: :request
19
+ config.include AppHelper, type: :request
20
+ end
@@ -0,0 +1,3 @@
1
+ class Simple::Application < Rodakase::Application
2
+ plugin :heartbeat
3
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'main/container'
2
+
3
+ Main::Container.finalize!
4
+
5
+ require 'main/application'
6
+ require 'main/view'
7
+ require 'main/requests'
8
+
9
+ Main::Container.require('requests/**/*.rb')
@@ -0,0 +1,17 @@
1
+ require 'rodakase/application'
2
+ require_relative 'container'
3
+
4
+ module Main
5
+ class Application < Rodakase::Application
6
+ configure do |config|
7
+ config.routes = 'web/routes'.freeze
8
+ config.container = Main::Container
9
+ end
10
+
11
+ route do |r|
12
+ r.multi_route
13
+ end
14
+
15
+ load_routes!
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ require 'rodakase/container'
2
+
3
+ module Main
4
+ class Container < Rodakase::Container
5
+ setting :root, Pathname(__FILE__).join('../..').realpath.dirname.freeze
6
+ setting :auto_register, 'lib'
7
+
8
+ configure do
9
+ load_paths!('lib')
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'container'
2
+
3
+ module Main
4
+ Import = Main::Container.import_module
5
+
6
+ def self.Import(*args)
7
+ Import[*args]
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'container'
2
+
3
+ module Main
4
+ module Requests
5
+ class Registrar
6
+ attr_reader :container
7
+
8
+ def initialize(container)
9
+ @container = container
10
+ end
11
+
12
+ def define(identifier, &block)
13
+ container.register(identifier, container[:transaction].define(&block))
14
+ end
15
+ end
16
+
17
+ def self.define(&block)
18
+ yield(Registrar.new(Container))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'rodakase/view'
2
+ require_relative 'container'
3
+
4
+ module Main
5
+ class Page
6
+ def title
7
+ 'Woohaa'
8
+ end
9
+ end
10
+ end
11
+
12
+ module Main
13
+ class View < Rodakase::View::Layout
14
+ setting :root, Container.root.join('web/templates')
15
+ setting :scope, Page.new
16
+ setting :engine, :slim
17
+ setting :name, 'app'.freeze
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Entities
2
+ User = Struct.new(:id, :name)
3
+ end
@@ -0,0 +1,14 @@
1
+ require 'main/import'
2
+ require 'entities/user'
3
+
4
+ module Persistence
5
+ module Repositories
6
+ class Users
7
+ include Main::Import(:db)
8
+
9
+ def all
10
+ db[:users]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ require 'rodakase/transaction'
2
+
3
+ class Transaction
4
+ def success(*args)
5
+ Rodakase::Transaction.Success(*args)
6
+ end
7
+
8
+ def failure(*args)
9
+ Rodakase::Transaction.Failure(*args)
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ require 'main/import'
2
+ require 'entities/user'
3
+ require 'transaction'
4
+
5
+ module Transactions
6
+ class RegisterUser < Transaction
7
+ include Main::Import(:db)
8
+
9
+ def call(params)
10
+ if params['name']
11
+ success(db[:users] << Entities::User.new(*params.values_at('id', 'name')))
12
+ else
13
+ failure(:validation, 'name is missing')
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ require 'main/view'
2
+
3
+ module Views
4
+ module Users
5
+ class Index < Main::View
6
+ configure do |config|
7
+ config.template = 'users/index'
8
+ end
9
+
10
+ def locals(options)
11
+ { users: [{ name: 'Jane' }, { name: 'Joe' }] }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ Main::Requests.define do |r|
2
+ r.define('requests.users.create') do
3
+ step 'transactions.register_user'
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ class Main::Application < Rodakase::Application
2
+ route('users') do |r|
3
+ r.get(to: 'views.users.index')
4
+
5
+ r.resolve('requests.users.create') do |t|
6
+ r.post do
7
+ t.(r[:user]) do |m|
8
+ m.success do
9
+ response.status = 201
10
+ end
11
+
12
+ m.failure do |err|
13
+ err.on(:validation) do |v|
14
+ r.redirect '/users'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end