happy 0.0.1 → 0.1.0.pre.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.
data/happy.gemspec CHANGED
@@ -14,4 +14,9 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "happy"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Happy::VERSION
17
+
18
+ gem.add_dependency 'activesupport', '~> 3.1'
19
+ gem.add_dependency 'rack', '~> 1.4'
20
+ gem.add_dependency 'happy-helpers' # TODO: , '~> 0.1.0'
21
+ gem.add_dependency 'allowance', '>= 0.1.1'
17
22
  end
@@ -0,0 +1,78 @@
1
+ module Happy
2
+ module Actions
3
+ def serve!(data, options = {})
4
+ # Don't serve if there are still bits of path remaining.
5
+ return unless remaining_path.empty?
6
+
7
+ # Don't serve is data is not a string.
8
+ return unless data.is_a?(String)
9
+
10
+ # Mix in default options
11
+ options = {
12
+ layout: context.layout
13
+ }.merge(options)
14
+
15
+ # Add optional headers et al
16
+ response.status = options[:status] if options.has_key?(:status)
17
+ response['Content-type'] = options[:content_type] if options.has_key?(:content_type)
18
+
19
+ # Apply layout, if available
20
+ if options[:layout]
21
+ data = render(options[:layout]) { data }
22
+ end
23
+
24
+ # Set response body and finish request
25
+ response.body = [data]
26
+ halt!
27
+ end
28
+
29
+ def halt!(message = :done)
30
+ throw message
31
+ end
32
+
33
+ def redirect!(to, status = 302)
34
+ header "Location", url_for(to)
35
+ response.status = status
36
+ halt!
37
+ end
38
+
39
+ def layout(name)
40
+ context.layout = name
41
+ end
42
+
43
+ def content_type(type)
44
+ header 'Content-type', type
45
+ end
46
+
47
+ def max_age(t, options = {})
48
+ options = {
49
+ :public => true,
50
+ :must_revalidate => true
51
+ }.merge(options)
52
+
53
+ s = []
54
+ s << 'public' if options[:public]
55
+ s << 'must-revalidate' if options[:must_revalidate]
56
+ s << "max-age=#{t.to_i}"
57
+
58
+ cache_control s.join(', ')
59
+ end
60
+
61
+ def cache_control(s)
62
+ header 'Cache-control', s
63
+ end
64
+
65
+ def header(name, value)
66
+ response[name] = value
67
+ end
68
+
69
+ def invoke(klass, options = {}, &blk)
70
+ klass.new(env, options, &blk).perform
71
+ end
72
+
73
+ def run(app)
74
+ context.response = app.call(request.env)
75
+ halt!
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,47 @@
1
+ require 'happy/request'
2
+ require 'happy/helpers'
3
+
4
+ module Happy
5
+ class Context
6
+ include Helpers
7
+
8
+ attr_reader :request, :response, :remaining_path
9
+ attr_accessor :layout, :controller
10
+ delegate :params, :session, :to => :request
11
+
12
+ def initialize(request, response)
13
+ @request = request
14
+ @response = response
15
+ @remaining_path = @request.path.split('/').reject {|s| s.blank? }
16
+ @layout = nil
17
+ @controller = nil
18
+ end
19
+
20
+ def with_controller(new_controller)
21
+ # remember previous controller
22
+ old_controller = self.controller
23
+ self.controller = new_controller
24
+
25
+ # execute permissions block
26
+ controller.class.permissions_blk.try(:call, permissions, self)
27
+
28
+ # execute block
29
+ yield
30
+ ensure
31
+ # switch back to previous controller
32
+ self.controller = old_controller
33
+ end
34
+
35
+ def response=(r)
36
+ @response = r
37
+ end
38
+
39
+ private
40
+
41
+ class << self
42
+ def from_env(env)
43
+ new(Happy::Request.new(env), Rack::Response.new)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ require 'happy/routing'
2
+ require 'happy/actions'
3
+ require 'happy/rackable'
4
+
5
+ module Happy
6
+ class Controller
7
+ include Routing
8
+ include Actions
9
+ include Rackable
10
+
11
+ attr_reader :options, :env
12
+
13
+ delegate :request, :response, :remaining_path, :params, :session,
14
+ :render, :url_for,
15
+ :to => :context
16
+
17
+ def initialize(env = nil, options = {}, &blk)
18
+ @env = env
19
+ @options = options
20
+ instance_exec(&blk) if blk
21
+ end
22
+
23
+ def perform
24
+ context.with_controller(self) do
25
+ route
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def context
32
+ @env['happy.context'] ||= self.class.context_class.from_env(@env)
33
+ end
34
+
35
+ def route
36
+ instance_exec(&self.class.route_blk) if self.class.route_blk
37
+ end
38
+
39
+ class << self
40
+ attr_reader :route_blk
41
+
42
+ def route(&blk)
43
+ @route_blk = blk
44
+ end
45
+
46
+ def context(&blk)
47
+ context_class.class_exec(&blk)
48
+ end
49
+
50
+ def context_class
51
+ Happy::Context
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ require 'happy-helpers'
2
+
3
+ module Happy
4
+ module Helpers
5
+ include HappyHelpers::Helpers
6
+
7
+ def render(what, options = {}, &blk)
8
+ case what
9
+ when String then render_template(what, options, &blk)
10
+ when Enumerable then what.map { |i| render(i, options, &blk) }.join
11
+ else render_resource(what, options)
12
+ end
13
+ end
14
+
15
+ def render_template(name, variables = {}, &blk)
16
+ HappyHelpers::Templates.render(name, self, variables, &blk)
17
+ end
18
+
19
+ def render_resource(resource, options = {})
20
+ # build name strings
21
+ singular_name = resource.class.to_s.tableize.singularize
22
+ plural_name = singular_name.pluralize
23
+
24
+ # set options
25
+ options = {
26
+ singular_name => resource
27
+ }.merge(options)
28
+
29
+ # render
30
+ render_template("#{plural_name}/_#{singular_name}.html.haml", options)
31
+ end
32
+
33
+ alias_method :h, :escape_html
34
+ alias_method :l, :localize
35
+ alias_method :t, :translate
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ module Happy
2
+ module Permissions
3
+ module ContextExtensions
4
+ extend ActiveSupport::Concern
5
+
6
+ def permissions(&blk)
7
+ @permissions ||= Allowance.define
8
+ end
9
+
10
+ def can?(*args)
11
+ permissions.allowed?(*args)
12
+ end
13
+ end
14
+
15
+ module ControllerExtensions
16
+ extend ActiveSupport::Concern
17
+
18
+ included do
19
+ delegate :can?, :to => :context
20
+ end
21
+
22
+ module ClassMethods
23
+ attr_accessor :permissions_blk
24
+
25
+ def permissions(&blk)
26
+ self.permissions_blk = blk
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Happy::Context.send(:include, Happy::Permissions::ContextExtensions)
34
+ Happy::Controller.send(:include, Happy::Permissions::ControllerExtensions)
@@ -0,0 +1,24 @@
1
+ module Happy
2
+ module Rackable
3
+ extend ActiveSupport::Concern
4
+
5
+ def call(env)
6
+ @env = env
7
+
8
+ catch :done do
9
+ serve! perform
10
+
11
+ # If we get here, #serve decided not to serve.
12
+ raise NotFoundError
13
+ end
14
+
15
+ response
16
+ end
17
+
18
+ module ClassMethods
19
+ def call(env)
20
+ new.call(env)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ require 'happy-helpers/utils/date_parameter_converter'
2
+
3
+ module Happy
4
+ class Request < Rack::Request
5
+ protected
6
+
7
+ def parse_query(qs)
8
+ super(qs).tap do |p|
9
+ HappyHelpers::Utils::DateParameterConverter.convert!(p)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,112 @@
1
+ module Happy
2
+ module Resources
3
+ module ControllerExtensions
4
+ def resource(klass, options = {}, &blk)
5
+ invoke Happy::Resources::ResourceMounter, options.merge(:class => klass), &blk
6
+ end
7
+ end
8
+
9
+ class ResourceMounter < Happy::Controller
10
+ def render_resource_template(name)
11
+ render "#{options[:plural_name]}/#{name}.html.haml"
12
+ end
13
+
14
+ def resource
15
+ options[:class]
16
+ end
17
+
18
+ def resource_with_permission_scope(*args)
19
+ context.permissions.scoped_model(*args, options[:class])
20
+ end
21
+
22
+ def require_permission!(*args)
23
+ raise "not allowed" unless can?(*args, options[:class])
24
+ end
25
+
26
+ def set_plural_variable(v)
27
+ context.instance_variable_set "@#{options[:plural_name]}", v
28
+ end
29
+
30
+ def plural_variable
31
+ context.instance_variable_get "@#{options[:plural_name]}"
32
+ end
33
+
34
+ def set_singular_variable(v)
35
+ context.instance_variable_set "@#{options[:singular_name]}", v
36
+ end
37
+
38
+ def singular_variable
39
+ context.instance_variable_get "@#{options[:singular_name]}"
40
+ end
41
+
42
+ def do_index
43
+ require_permission! :index
44
+ set_plural_variable resource_with_permission_scope(:index).all
45
+ render_resource_template 'index'
46
+ end
47
+
48
+ def do_show
49
+ require_permission! :show
50
+ set_singular_variable resource_with_permission_scope(:show).find(params['id'])
51
+ render_resource_template 'show'
52
+ end
53
+
54
+ def do_new
55
+ require_permission! :new
56
+ set_singular_variable resource_with_permission_scope(:new).new(params[options[:singular_name]], :as => options[:role])
57
+ render_resource_template 'new'
58
+ end
59
+
60
+ def do_create
61
+ require_permission! :create
62
+ set_singular_variable resource_with_permission_scope(:create).new(params[options[:singular_name]], :as => options[:role])
63
+
64
+ if singular_variable.save
65
+ redirect! singular_variable
66
+ else
67
+ render_resource_template 'new'
68
+ end
69
+ end
70
+
71
+ def do_edit
72
+ require_permission! :edit
73
+ set_singular_variable resource_with_permission_scope(:edit).find(params['id'])
74
+ render_resource_template 'edit'
75
+ end
76
+
77
+ def do_update
78
+ require_permission! :update
79
+ set_singular_variable resource_with_permission_scope(:update).find(params['id'])
80
+ singular_variable.assign_attributes params[options[:singular_name]], :as => options[:role]
81
+
82
+ if singular_variable.save
83
+ redirect! singular_variable
84
+ else
85
+ render_resource_template 'edit'
86
+ end
87
+ end
88
+
89
+ def route
90
+ @options = {
91
+ singular_name: options[:class].to_s.tableize.singularize,
92
+ plural_name: options[:class].to_s.tableize.pluralize
93
+ }.merge(@options)
94
+
95
+ path options[:plural_name] do
96
+ get('new') { do_new }
97
+
98
+ path :id do
99
+ get { do_show }
100
+ post { do_update }
101
+ get('edit') { do_edit }
102
+ end
103
+
104
+ post { do_create }
105
+ get { do_index }
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ Happy::Controller.send(:include, Happy::Resources::ControllerExtensions)
@@ -0,0 +1,56 @@
1
+ module Happy
2
+ module Routing
3
+ def path_to_regexp(path)
4
+ path = ":#{path}" if path.is_a?(Symbol)
5
+ Regexp.compile('^'+path.gsub(/\)/, ')?').gsub(/\//, '\/').gsub(/\./, '\.').gsub(/:(\w+)/, '(?<\\1>.+)')+'$')
6
+ end
7
+
8
+ def path(*args, &blk)
9
+ options = (args.pop if args.last.is_a?(Hash)) || {}
10
+ args = [nil] if args.empty?
11
+
12
+ args.each do |name|
13
+ if name.present?
14
+ path_match = path_to_regexp(name).match(remaining_path.first)
15
+ end
16
+
17
+ method_matched = [nil, request.request_method.downcase.to_sym].include?(options[:method])
18
+ path_matched = (path_match || (name.nil? && remaining_path.empty?))
19
+
20
+ # Only do something here if method and requested path both match
21
+ if path_matched && method_matched
22
+ # Transfer variables contained in path name to params hash
23
+ if path_match
24
+ path_match.names.each { |k| request.params[k] = path_match[k] }
25
+ remaining_path.shift
26
+ end
27
+
28
+ serve! instance_exec(&blk)
29
+
30
+ # If we get here, #serve decided not to serve.
31
+ raise NotFoundError
32
+ end
33
+ end
34
+ end
35
+
36
+ def get(*args, &blk)
37
+ args.last.is_a?(Hash) ? args.last.merge(method: :get) : args.push(method: :get)
38
+ path(*args, &blk)
39
+ end
40
+
41
+ def post(*args, &blk)
42
+ args.last.is_a?(Hash) ? args.last.merge(method: :post) : args.push(method: :post)
43
+ path(*args, &blk)
44
+ end
45
+
46
+ def put(*args, &blk)
47
+ args.last.is_a?(Hash) ? args.last.merge(method: :put) : args.push(method: :put)
48
+ path(*args, &blk)
49
+ end
50
+
51
+ def delete(*args, &blk)
52
+ args.last.is_a?(Hash) ? args.last.merge(method: :delete) : args.push(method: :delete)
53
+ path(*args, &blk)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ module Happy
2
+ class Static < Happy::Controller
3
+ def route
4
+ run Rack::File.new(options[:path])
5
+ end
6
+ end
7
+ end
data/lib/happy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Happy
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0.pre.1"
3
3
  end
data/lib/happy.rb CHANGED
@@ -1,5 +1,14 @@
1
- require "happy/version"
1
+ require 'active_support/all' # SMELL
2
+
3
+ require 'happy/context'
4
+ require 'happy/controller'
5
+ require 'happy/static'
2
6
 
3
7
  module Happy
4
- # Your code goes here...
8
+ class HappyError < StandardError ; end
9
+ class NotFoundError < HappyError ; end
10
+
11
+ def self.env
12
+ ActiveSupport::StringInquirer.new(ENV['RACK_ENV'] || 'development')
13
+ end
5
14
  end
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: happy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.1.0.pre.1
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Hendrik Mans
@@ -10,7 +10,51 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
  date: 2012-05-31 00:00:00.000000000 Z
13
- dependencies: []
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &70164769147200 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70164769147200
25
+ - !ruby/object:Gem::Dependency
26
+ name: rack
27
+ requirement: &70164769146300 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '1.4'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70164769146300
36
+ - !ruby/object:Gem::Dependency
37
+ name: happy-helpers
38
+ requirement: &70164769145860 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70164769145860
47
+ - !ruby/object:Gem::Dependency
48
+ name: allowance
49
+ requirement: &70164769145220 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.1
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70164769145220
14
58
  description: A happy little toolkit for writing web applications.
15
59
  email:
16
60
  - hendrik@mans.de
@@ -25,6 +69,16 @@ files:
25
69
  - Rakefile
26
70
  - happy.gemspec
27
71
  - lib/happy.rb
72
+ - lib/happy/actions.rb
73
+ - lib/happy/context.rb
74
+ - lib/happy/controller.rb
75
+ - lib/happy/helpers.rb
76
+ - lib/happy/permissions.rb
77
+ - lib/happy/rackable.rb
78
+ - lib/happy/request.rb
79
+ - lib/happy/resources.rb
80
+ - lib/happy/routing.rb
81
+ - lib/happy/static.rb
28
82
  - lib/happy/version.rb
29
83
  homepage: https://github.com/hmans/happy
30
84
  licenses: []
@@ -41,9 +95,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
41
95
  required_rubygems_version: !ruby/object:Gem::Requirement
42
96
  none: false
43
97
  requirements:
44
- - - ! '>='
98
+ - - ! '>'
45
99
  - !ruby/object:Gem::Version
46
- version: '0'
100
+ version: 1.3.1
47
101
  requirements: []
48
102
  rubyforge_project:
49
103
  rubygems_version: 1.8.11