culpa 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b2243f4c6c5fc62bc2973a233a454bb21796aa24
4
+ data.tar.gz: 57b0f85fffd6eb328cbadd4cf29c4bb6dc0e2f33
5
+ SHA512:
6
+ metadata.gz: 748036cb1c35be6eebeeec898b78da3a807606d5188251ab4e5bc71a245152f02379dc7e7ee0b12698807768f6e1b7a4363378f87e636703e132bed99e8920f7
7
+ data.tar.gz: 243bd8206d143a2853f9a9669f2378d41c8480951c37e432c3d1d11fba77c0b07205264a514ce6547341b3b839c62e40cc1d968a629e28c3034a6924960d714d
data/bin/culpa ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+
5
+ module Actions
6
+ Dir['./actions/*.rb'].each do |file|
7
+ eval(File.open(file).read)
8
+ end
9
+ end
10
+
11
+ module Models
12
+ Dir['./models/*.rb'].each do |file|
13
+ eval(File.open(file).read)
14
+ end
15
+ end
16
+
17
+ def create_project(project_path)
18
+ puts "Initializing Culpa project in #{project_path}"
19
+
20
+ unless Dir.exist? project_path
21
+ FileUtils.mkdir_p project_path
22
+ puts '==> Creating folder'
23
+ end
24
+
25
+ puts '==> Create directory structure'
26
+ FileUtils.mkdir "#{project_path}/actions"
27
+ FileUtils.touch "#{project_path}/actions/.keep"
28
+ FileUtils.mkdir "#{project_path}/models"
29
+ FileUtils.touch "#{project_path}/models/.keep"
30
+ FileUtils.mkdir "#{project_path}/config"
31
+ FileUtils.touch "#{project_path}/config/router.yml"
32
+ FileUtils.mkdir "#{project_path}/config/initializers"
33
+ FileUtils.touch "#{project_path}/config/initializers/.keep"
34
+
35
+ puts '==> Copying standard gemfile'
36
+ gemfile_path = File.join( File.dirname(__FILE__), '../templates/Gemfile' )
37
+ FileUtils.cp gemfile_path, "#{project_path}/Gemfile"
38
+
39
+ puts '==> Copying standard config.ru'
40
+ config_rackup_path = File.join( File.dirname(__FILE__), '../templates/config.ru' )
41
+ FileUtils.cp config_rackup_path, "#{project_path}/config.ru"
42
+ end
43
+
44
+ def open_console
45
+ require 'rubygems'
46
+ require 'bundler/setup'
47
+ require 'yaml'
48
+ require 'culpa'
49
+ require 'pry'
50
+ binding.pry
51
+ end
52
+
53
+ def display_help
54
+ puts 'Welcome to Culpa ! The MEA framework.'
55
+ puts 'Usage : culpa [command] [args 1..n]'
56
+ puts 'Commands supported :'
57
+ puts ' - new [folder] : Create a new culpa project on the specified folder'
58
+ puts ' - console : Drop an interactive shell within the running directory'
59
+ puts ''
60
+ puts 'This software is distributed under the MIT license, available here : '
61
+ puts ' - https://raw.githubusercontent.com/HipsterWhale/culpa/master/LICENSE'
62
+ end
63
+
64
+ case ARGV[0]
65
+ when 'new'
66
+ create_project ARGV[1]
67
+ when 'console'
68
+ open_console
69
+ else
70
+ display_help
71
+ end
data/lib/action.rb ADDED
@@ -0,0 +1,20 @@
1
+ module Actions
2
+
3
+ class Action
4
+
5
+ attr_accessor :to_render
6
+
7
+ def initialize(envelope, request)
8
+ @e = envelope
9
+ @r = request
10
+ @to_render = nil
11
+ end
12
+
13
+ def render(response_object, options={})
14
+ @to_render = {object: response_object, options: options}
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
data/lib/culpa.rb ADDED
@@ -0,0 +1,147 @@
1
+ require 'bundler'
2
+ require 'envelope'
3
+ require 'action'
4
+ require 'json'
5
+
6
+ module Culpa
7
+
8
+ ROUTE_PATTERNS = {
9
+ '/:res_name' => {
10
+ regex: Regexp.new('^/(\w+)$'),
11
+ id_to_sym: {
12
+ 1 => :res_name
13
+ }
14
+ },
15
+ '/:res_name/:id' => {
16
+ regex: Regexp.new('^/(\w+)/(\w+)$'),
17
+ id_to_sym: {
18
+ 1 => :res_name,
19
+ 2 => :id
20
+ }
21
+ },
22
+ '/:res_name/:id/:sub_call' => {
23
+ regex: Regexp.new('^/(\w+)/(\w+)/(\w+)$'),
24
+ id_to_sym: {
25
+ 1 => :res_name,
26
+ 2 => :id,
27
+ 3 => :sub_call
28
+ }
29
+ }
30
+ }
31
+
32
+ class Application
33
+
34
+ class UnpredictableSubCallError < StandardError; end
35
+ class NoRenderCalled < StandardError; end
36
+
37
+ def initialize(router = {})
38
+ @router = router
39
+ end
40
+
41
+ # Router's helpers
42
+ def call_action(options)
43
+ # Preparing common parts
44
+ router_method_name = "#{options[:sub_call]}_#{options[:res_name]}"
45
+ envelope = Envelope.new
46
+ request = EnvelopeRequest.new(options)
47
+
48
+ # The router do not have the key
49
+ if !@router || !@router.has_key?(router_method_name)
50
+ default_class = options[:res_name].split('_').map{|w| w.capitalize}.join
51
+ action_default_class = Actions.const_get(default_class).new(envelope, request)
52
+ if action_default_class && action_default_class.respond_to?(options[:sub_call])
53
+ action_default_class.send(options[:sub_call])
54
+ if action_default_class.to_render
55
+ return do_render action_default_class.to_render if action_default_class.to_render
56
+ else
57
+ raise NoRenderCalled.new
58
+ end
59
+ end
60
+ return not_found
61
+ end
62
+
63
+ # The router have the key
64
+ @router[router_method_name].each do |method|
65
+ action_class_name, method_name = method.split('.')
66
+ action_class = Actions.const_get(action_class_name).new(envelope, request)
67
+ action_class.send method_name
68
+ return do_render action_class.to_render if action_class.to_render
69
+ end
70
+
71
+ # If nothing called do_render...
72
+ raise NoRenderCalled.new
73
+ end
74
+
75
+ def do_render(to_render)
76
+ code = to_render[:options][:status] || 200
77
+ content_type = to_render[:options][:content_type] || 'application/json'
78
+ [code.to_s, {'Content-Type' => content_type}, [to_render[:object].to_json]]
79
+ end
80
+
81
+ def call(env)
82
+ call_options = {
83
+ verb: env['REQUEST_METHOD'].downcase.to_sym
84
+ }
85
+ if env['rack.input'].is_a? Hash
86
+ if env['rack.input'].has_key? :sub_call
87
+ call_options[:sub_call] = env['rack.input'][:sub_call]
88
+ end
89
+ call_options[:input] = env['rack.input'][:data]
90
+ else
91
+ call_options[:input] = env['rack.input']
92
+ end
93
+ if !(route_params = parse_path('/:res_name', env['PATH_INFO'])).nil?
94
+ call_options[:res_name] = route_params[:res_name]
95
+ case call_options[:verb]
96
+ when :get
97
+ call_options[:sub_call] = 'list'
98
+ when :post
99
+ call_options[:sub_call] = 'create'
100
+ else
101
+ raise UnpredictableSubCallError.new
102
+ end unless call_options.has_key? :sub_call
103
+ elsif !(route_params = parse_path('/:res_name/:id', env['PATH_INFO'])).nil?
104
+ call_options[:res_name] = route_params[:res_name]
105
+ call_options[:id] = route_params[:id]
106
+ case call_options[:verb]
107
+ when :get
108
+ call_options[:sub_call] = 'get'
109
+ when :put
110
+ call_options[:sub_call] = 'update'
111
+ when :delete
112
+ call_options[:sub_call] = 'delete'
113
+ else
114
+ raise UnpredictableSubCallError.new
115
+ end unless call_options.has_key? :sub_call
116
+ elsif !(route_params = parse_path('/:res_name/:id/:sub_call', env['PATH_INFO'])).nil?
117
+ call_options[:res_name] = route_params[:res_name]
118
+ call_options[:id] = route_params[:id]
119
+ call_options[:sub_call] = route_params[:sub_call]
120
+ else
121
+ return bad_request
122
+ end
123
+ call_action call_options
124
+ end
125
+
126
+ def parse_path(pattern, path)
127
+ current_pattern = ROUTE_PATTERNS[pattern]
128
+ result = path.match(current_pattern[:regex])
129
+ return unless result
130
+ returned = {}
131
+ current_pattern[:id_to_sym].each do |id, sym|
132
+ returned[sym] = result[id]
133
+ end
134
+ returned
135
+ end
136
+
137
+ def bad_request
138
+ ['400', {'Content-Type' => 'application/json'}, ['{ "error": "bad_request" }']]
139
+ end
140
+
141
+ def not_found
142
+ ['404', {'Content-Type' => 'application/json'}, ['{ "error": "not_found" }']]
143
+ end
144
+
145
+ end
146
+
147
+ end
data/lib/envelope.rb ADDED
@@ -0,0 +1,45 @@
1
+ module Culpa
2
+
3
+ class MustHaveFailed < StandardError; end
4
+
5
+ class Envelope
6
+
7
+ def must_have!(*args)
8
+ args.each do |arg|
9
+ raise MustHaveFailed.new("Envelope must_have! failed on #{arg}") unless instance_variable_defined? "@#{arg}"
10
+ end
11
+ end
12
+
13
+ def method_missing(sym, *args)
14
+ sym = sym.to_s
15
+ if sym.end_with? '='
16
+ instance_variable_set "@#{sym.gsub('=','')}", args[0]
17
+ else
18
+ instance_variable_get "@#{sym.gsub('=','')}"
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ class EnvelopeRequest
25
+
26
+ attr_accessor :verb
27
+ attr_accessor :action
28
+ attr_accessor :res_name
29
+ attr_accessor :id
30
+ attr_accessor :body
31
+ attr_accessor :input
32
+
33
+ def initialize(attributes)
34
+ attributes.each do |name, value|
35
+ instance_variable_set "@#{name}", value
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+
44
+
45
+
data/templates/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'culpa', '~> 0.0'
4
+
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'yaml'
4
+ require 'culpa'
5
+
6
+ module Actions
7
+ Dir['./actions/*.rb'].each do |file|
8
+ eval(File.open(file).read)
9
+ end
10
+ end
11
+
12
+ module Models
13
+ Dir['./models/*.rb'].each do |file|
14
+ eval(File.open(file).read)
15
+ end
16
+ end
17
+
18
+ run Culpa::Application.new(YAML.load_file('./config/router.yml'))
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: culpa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Jérémy SEBAN
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.10'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.10'
41
+ description: Culpa is a try of following MEA principles.
42
+ email: jeremy@seban.eu
43
+ executables:
44
+ - culpa
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - bin/culpa
49
+ - lib/action.rb
50
+ - lib/culpa.rb
51
+ - lib/envelope.rb
52
+ - templates/Gemfile
53
+ - templates/config.ru
54
+ homepage:
55
+ licenses:
56
+ - MIT
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 2.6.4
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: 'Culpa : the MEA framework'
78
+ test_files: []