pancake 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/bin/pancake +96 -0
  2. data/lib/pancake.rb +5 -7
  3. data/lib/pancake/configuration.rb +2 -23
  4. data/lib/pancake/core_ext/object.rb +1 -1
  5. data/lib/pancake/defaults/configuration.rb +21 -1
  6. data/lib/pancake/generators.rb +0 -3
  7. data/lib/pancake/hooks/inheritable_inner_classes.rb +15 -2
  8. data/lib/pancake/master.rb +34 -1
  9. data/lib/pancake/mixins/render.rb +1 -2
  10. data/lib/pancake/mixins/stack_helper.rb +1 -1
  11. data/lib/pancake/router.rb +18 -63
  12. data/lib/pancake/stack/configuration.rb +3 -5
  13. data/lib/pancake/stack/router.rb +10 -33
  14. data/lib/pancake/stack/stack.rb +70 -50
  15. data/{spec/helpers → lib/pancake/test}/helpers.rb +3 -3
  16. data/lib/pancake/test/matchers.rb +20 -0
  17. data/lib/pancake/vendor/hashie/.document +5 -0
  18. data/lib/pancake/vendor/hashie/.gitignore +7 -0
  19. data/lib/pancake/vendor/hashie/Gemfile +11 -0
  20. data/lib/pancake/vendor/hashie/Gemfile.lock +25 -0
  21. data/lib/pancake/{generators/templates/short/%stack_name%/LICENSE.tt → vendor/hashie/LICENSE} +1 -1
  22. data/lib/pancake/vendor/hashie/README.rdoc +101 -0
  23. data/lib/pancake/{generators/templates/micro/%stack_name%/Rakefile.tt → vendor/hashie/Rakefile} +3 -13
  24. data/lib/pancake/vendor/hashie/VERSION +1 -0
  25. data/lib/pancake/vendor/hashie/hashie.gemspec +33 -0
  26. data/lib/pancake/vendor/hashie/lib/hashie.rb +5 -0
  27. data/lib/pancake/vendor/hashie/lib/hashie/clash.rb +86 -0
  28. data/lib/pancake/vendor/hashie/lib/hashie/dash.rb +108 -0
  29. data/lib/pancake/vendor/hashie/lib/hashie/hash.rb +22 -0
  30. data/lib/pancake/vendor/hashie/lib/hashie/hash_extensions.rb +49 -0
  31. data/lib/pancake/vendor/hashie/lib/hashie/mash.rb +148 -0
  32. data/lib/pancake/vendor/hashie/spec/hashie/clash_spec.rb +42 -0
  33. data/lib/pancake/vendor/hashie/spec/hashie/dash_spec.rb +103 -0
  34. data/lib/pancake/vendor/hashie/spec/hashie/hash_spec.rb +22 -0
  35. data/lib/pancake/vendor/hashie/spec/hashie/mash_spec.rb +135 -0
  36. data/lib/pancake/vendor/hashie/spec/spec.opts +2 -0
  37. data/lib/pancake/{generators/templates/short/%stack_name%/spec/spec_helper.rb.tt → vendor/hashie/spec/spec_helper.rb} +7 -7
  38. data/spec/pancake/configuration_spec.rb +1 -1
  39. data/spec/pancake/constants_spec.rb +1 -1
  40. data/spec/pancake/defaults/configuration_spec.rb +1 -1
  41. data/spec/pancake/hooks/on_inherit_spec.rb +13 -13
  42. data/spec/pancake/inheritance_spec.rb +22 -22
  43. data/spec/pancake/middleware_spec.rb +6 -5
  44. data/spec/pancake/middlewares/logger_spec.rb +1 -1
  45. data/spec/pancake/middlewares/static_spec.rb +1 -1
  46. data/spec/pancake/mime_types_spec.rb +1 -1
  47. data/spec/pancake/mixins/publish_spec.rb +24 -24
  48. data/spec/pancake/mixins/render/template_spec.rb +1 -1
  49. data/spec/pancake/mixins/render/view_context_spec.rb +1 -1
  50. data/spec/pancake/mixins/render_spec.rb +1 -1
  51. data/spec/pancake/mixins/request_helper_spec.rb +1 -1
  52. data/spec/pancake/mixins/stack_helper_spec.rb +3 -3
  53. data/spec/pancake/pancake_spec.rb +1 -1
  54. data/spec/pancake/paths_spec.rb +30 -30
  55. data/spec/pancake/stack/router_spec.rb +24 -62
  56. data/spec/pancake/stack/stack_configuration_spec.rb +1 -1
  57. data/spec/pancake/stack/stack_spec.rb +47 -4
  58. data/spec/spec_helper.rb +3 -3
  59. metadata +56 -128
  60. data/bin/pancake-gen +0 -30
  61. data/lib/pancake/bootloaders.rb +0 -187
  62. data/lib/pancake/generators/base.rb +0 -12
  63. data/lib/pancake/generators/micro_generator.rb +0 -17
  64. data/lib/pancake/generators/short_generator.rb +0 -18
  65. data/lib/pancake/generators/templates/common/dotgitignore +0 -22
  66. data/lib/pancake/generators/templates/common/dothtaccess +0 -17
  67. data/lib/pancake/generators/templates/micro/%stack_name%/%stack_name%.rb.tt +0 -8
  68. data/lib/pancake/generators/templates/micro/%stack_name%/config.ru.tt +0 -12
  69. data/lib/pancake/generators/templates/micro/%stack_name%/pancake_init.rb.tt +0 -1
  70. data/lib/pancake/generators/templates/micro/%stack_name%/public/.empty_directory +0 -0
  71. data/lib/pancake/generators/templates/micro/%stack_name%/tmp/.empty_directory +0 -0
  72. data/lib/pancake/generators/templates/micro/%stack_name%/views/root.html.haml +0 -1
  73. data/lib/pancake/generators/templates/short/%stack_name%/README.tt +0 -7
  74. data/lib/pancake/generators/templates/short/%stack_name%/Rakefile.tt +0 -56
  75. data/lib/pancake/generators/templates/short/%stack_name%/VERSION.tt +0 -1
  76. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%.rb.tt +0 -12
  77. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/%stack_name%.rb.tt +0 -6
  78. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config.ru.tt +0 -10
  79. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/config.rb.tt +0 -23
  80. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +0 -15
  81. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +0 -16
  82. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/staging.rb.tt +0 -17
  83. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/models/.empty_directory +0 -0
  84. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/mounts/.empty_directory +0 -0
  85. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/public/.empty_directory +0 -0
  86. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tasks/%stack_name%.rake.tt +0 -4
  87. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/tmp/.empty_directory +0 -0
  88. data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/views/root.html.haml +0 -2
  89. data/lib/pancake/generators/templates/short/%stack_name%/pancake_init.rb.tt +0 -1
  90. data/lib/pancake/generators/templates/short/%stack_name%/spec/%stack_name%_spec.rb.tt +0 -11
  91. data/lib/pancake/stack/app.rb +0 -10
  92. data/lib/pancake/stack/bootloader.rb +0 -114
  93. data/lib/pancake/stack/middleware.rb +0 -0
  94. data/lib/pancake/stacks/short.rb +0 -3
  95. data/lib/pancake/stacks/short/bootloaders.rb +0 -5
  96. data/lib/pancake/stacks/short/controller.rb +0 -184
  97. data/lib/pancake/stacks/short/default/views/base.html.haml +0 -5
  98. data/lib/pancake/stacks/short/default/views/error.html.haml +0 -12
  99. data/lib/pancake/stacks/short/stack.rb +0 -207
  100. data/spec/helpers/matchers.rb +0 -25
  101. data/spec/pancake/bootloaders_spec.rb +0 -119
  102. data/spec/pancake/stack/app_spec.rb +0 -28
  103. data/spec/pancake/stack/bootloader_spec.rb +0 -41
  104. data/spec/pancake/stack/middleware_spec.rb +0 -0
  105. data/spec/pancake/stacks/short/controller_spec.rb +0 -442
  106. data/spec/pancake/stacks/short/middlewares_spec.rb +0 -22
  107. data/spec/pancake/stacks/short/router_spec.rb +0 -150
  108. data/spec/pancake/stacks/short/stack_spec.rb +0 -117
@@ -28,18 +28,16 @@ end # Pancake
28
28
  ####################
29
29
  # Setup the default configuration for each stack
30
30
  class Pancake::Stack::Configuration
31
- default :router, lambda{ _router }, "The router for this stack"
31
+ default :router, Proc.new{ _router }, "The router for this stack"
32
32
 
33
33
  def _router
34
34
  @_router ||= begin
35
35
  unless stack.nil?
36
- r = stack.router.dup
37
- r.configuration = self
38
- r.default(app)
36
+ r = stack.router.clone
37
+ r.stack = stack
39
38
  r.configuration = self
40
39
  r
41
40
  end
42
-
43
41
  end
44
42
  end
45
43
  end
@@ -1,44 +1,21 @@
1
1
  module Pancake
2
2
  class Stack
3
- class Router < Pancake::Router; end
4
3
  inheritable_inner_classes :Router
5
- cattr_writer :_router
6
-
7
- @_router = self::Router.new
4
+ class Router < Pancake::Router; end
8
5
 
9
- def self._router
10
- @_router ||= begin
11
- r = self::Router.new
12
- unless self == Pancake::Stack
13
- r.router = superclass._router.router.dup
6
+ def self.router
7
+ @router ||= begin
8
+ if superclass.respond_to?(:router) && superclass.router
9
+ r = superclass.router.clone(self::Router)
10
+ r.stack = self
11
+ else
12
+ r = self::Router.new
13
+ r.stack = self
14
14
  end
15
- r.stack = self
15
+ yield r if block_given?
16
16
  r
17
17
  end
18
18
  end
19
-
20
- # Resets the router to use the stacks namespaced router.
21
- # This allows a router to mixin a module, and have that module
22
- # mixed in to child stacks/routers. Effectively, this will reset the scope of inheritance so that a stack type can have particular route helpers
23
- #
24
- # When the router is rest, any routes declared in parent stacks will be lost.
25
- # @example
26
- # MyStack.reset_router! # => Replaces the current router with MyStack::Router (instance)
27
- #
28
- # @api public
29
- def self.reset_router!
30
- self._router = self::Router.new
31
- end
32
-
33
- def self.router
34
- yield _router if block_given?
35
- _router
36
- end
37
-
38
- def self.with_router
39
- yield router if block_given?
40
- router
41
- end
42
19
  end
43
20
  end
44
21
 
@@ -2,6 +2,11 @@ module Pancake
2
2
  class Stack
3
3
  attr_accessor :app_name
4
4
 
5
+ class_inheritable_array :_after_stack_initialize, :_after_build_stack, :_before_build_stack
6
+ self._after_stack_initialize = []
7
+ self._after_build_stack = []
8
+ self._before_build_stack = []
9
+
5
10
  # extend Hooks::InheritableInnerClasses
6
11
  extend Hooks::OnInherit
7
12
  extend Pancake::Middleware
@@ -10,9 +15,6 @@ module Pancake
10
15
  # Push the default paths in for this stack
11
16
  push_paths(:config, "config", "config.rb")
12
17
  push_paths(:config, "config/environments", "#{Pancake.env}.rb")
13
- push_paths(:models, "app/models", "**/*.rb")
14
- push_paths(:controllers, "app/controllers", "**/*.rb")
15
- push_paths(:router, "config", "router.rb")
16
18
  push_paths(:rake_tasks, "tasks", "**/*.rake")
17
19
  push_paths(:public, "public", "**/*")
18
20
  push_paths(:mounts, "mounts", "*/pancake_init.rb")
@@ -24,14 +26,35 @@ module Pancake
24
26
  raise "Stack root not set" if roots.empty?
25
27
  master = opts.delete(:master)
26
28
  set_as_master! if master
27
- # Run any :init level bootloaders for this stack
28
- self::BootLoader.run!(:stack_class => self, :only => {:level => :init})
29
- # Pick up any new stacks added during the boot process.
30
- set_as_master! if master
31
29
 
30
+ paths_for(:config ).each{ |f| require f.join }
31
+ paths_for(:mounts ).each{ |f| require f.join }
32
+ paths_for(:middleware ).each{ |f| require f.join }
33
+
34
+ router.mount_applications!
35
+
36
+ set_as_master! if master
32
37
  @initialized = true
38
+ # run the hook for after mounting
39
+ after_stack_initialize.each{ |blk| blk.call(self) }
33
40
  end # initiailze stack
34
41
 
42
+
43
+ def self.after_stack_initialize(&blk)
44
+ _after_stack_initialize << blk if blk
45
+ _after_stack_initialize
46
+ end
47
+
48
+ def self.after_build_stack(&blk)
49
+ _after_build_stack << blk if blk
50
+ _after_build_stack
51
+ end
52
+
53
+ def self.before_build_stack(&blk)
54
+ _before_build_stack << blk if blk
55
+ _before_build_stack
56
+ end
57
+
35
58
  # Adds the file to the stack root.
36
59
  #
37
60
  # @param file - The file identifier
@@ -47,6 +70,14 @@ module Pancake
47
70
  !!@initialized
48
71
  end
49
72
 
73
+ def self.include_pancake_stack!(include_it = true)
74
+ @include_pancake_stack = include_it
75
+ end
76
+
77
+ def self.include_pancake_stack?
78
+ !!@include_pancake_stack
79
+ end
80
+
50
81
  def initialize(app = nil, opts = {})
51
82
  @app_name = opts.delete(:app_name) || self.class
52
83
 
@@ -60,22 +91,46 @@ module Pancake
60
91
  self.configuration(@app_name)
61
92
  yield self.configuration(@app_name) if block_given?
62
93
 
63
- self.class::BootLoader.run!({
64
- :stack_class => self.class,
65
- :stack => self,
66
- :app => app,
67
- :app_name => @app_name,
68
- :except => {:level => :init}
69
- }.merge(opts))
94
+ @app ||= self.class.new_endpoint_instance
95
+
96
+ self.class.before_build_stack.each{|b| b.call(self, @app_name, opts)}
97
+
98
+ mwares = self.class.middlewares
99
+
100
+ @stack = Pancake::Middleware.build(@app, mwares)
101
+ app_config = Pancake.configuration.configs(@app_name)
102
+ app_config.stack = self.class
103
+ app_config.router.configuration = app_config
104
+ router = Pancake.configuration.configs[@app_name].router
105
+ router.mount_applications!
106
+ router.default(@stack)
107
+ self.class.after_build_stack.each{ |blk| blk.call(self, @app_name, opts) }
70
108
  end
71
109
 
72
110
  # Construct a stack using the application, wrapped in the middlewares
73
111
  # @api public
74
112
  def self.stackup(opts = {}, &block)
75
113
  app = new(nil, opts, &block)
76
- Pancake.configuration.configs[app.app_name].router
114
+ r = Pancake.configuration.configs[app.app_name].router
115
+ if include_pancake_stack?
116
+ Pancake.start{r}
117
+ else
118
+ r
119
+ end
77
120
  end # stackup
78
121
 
122
+ def call(env)
123
+ Pancake.configuration.configs[@app_name].router.call(env)
124
+ end
125
+
126
+
127
+ # get a new instance of the application for this stack
128
+ # Ovewrite this to provide custom application initialization
129
+ # :api: overwritable
130
+ def self.new_endpoint_instance
131
+ MISSING_APP
132
+ end
133
+
79
134
  # Loads the rake task for this stack, and all mounted stacks
80
135
  #
81
136
  # To have your rake task loaded include a "tasks" director in the stack root
@@ -204,45 +259,10 @@ module Pancake
204
259
  def self.base_template_name
205
260
  raise Errors::NotImplemented, "Stack may not be used for templates until it implements a base_template_name method"
206
261
  end
207
-
208
- # Creates a bootloader hook(s) of the given name. That are inheritable
209
- # This will create hooks for use in a bootloader (but will not create the bootloader itself!)
210
- #
211
- # @example
212
- # MyStack.create_bootloader_hook(:before_stuff, :after_stuff)
213
- #
214
- # MyStack.before_stuff do
215
- # # stuff to do before stuff
216
- # end
217
- #
218
- # MyStack.after_stuff do
219
- # # stuff to do after stuff
220
- # enc
221
- #
222
- # MyStack.before_stuff.each{|blk| blk.call}
223
- #
224
- # @api public
225
- def self.create_bootloader_hook(*hooks)
226
- hooks.each do |hook|
227
- extlib_inheritable_reader "_#{hook}"
228
- instance_variable_set("@_#{hook}", [])
229
-
230
- class_eval <<-RUBY
231
- def self.#{hook}(&blk)
232
- _#{hook} << blk if blk
233
- _#{hook}
234
- end
235
- RUBY
236
- end
237
- end
238
-
239
- create_bootloader_hook :before_build_stack, :before_mount_applications, :after_initialize_application, :after_build_stack, :before_stack_loads
240
262
  end # Stack
241
263
  end # Pancake
242
264
 
243
265
  require 'pancake/stack/configuration'
244
266
  require 'pancake/stack/router'
245
- require 'pancake/stack/bootloader'
246
- require 'pancake/stack/app'
247
267
  require 'pancake/defaults/middlewares'
248
268
  require 'pancake/defaults/configuration'
@@ -1,5 +1,5 @@
1
1
  module Pancake
2
- module Spec
2
+ module Test
3
3
  module Helpers
4
4
  def clear_constants(*classes)
5
5
  classes.flatten.each do |klass|
@@ -16,5 +16,5 @@ module Pancake
16
16
  Rack::MockRequest.env_for(path, opts)
17
17
  end
18
18
  end # Helpers
19
- end # Spec
20
- end # Pancake
19
+ end
20
+ end
@@ -34,6 +34,26 @@ module Pancake
34
34
  def mount(expected, path)
35
35
  MountMatcher.new(expected, path)
36
36
  end
37
+
38
+ class InheritFrom
39
+ def initialize(expected)
40
+ @expected = expected
41
+ end
42
+
43
+ def matches?(target)
44
+ @target = target
45
+ @target.ancestors.include?(@expected)
46
+ end
47
+
48
+ def failure_message
49
+ "expected #{@target} to inherit from #{@expected} but did not"
50
+ end
51
+ end
52
+
53
+ def inherit_from(expected)
54
+ InheritFrom.new(expected)
55
+ end
56
+
37
57
  end # Matchers
38
58
  end # Test
39
59
  end # Pancake
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ *.gem
7
+ .bundle
@@ -0,0 +1,11 @@
1
+ # A sample Gemfile
2
+ source :gemcutter
3
+
4
+ group :development do
5
+ gem 'rake'
6
+ gem 'json'
7
+ end
8
+
9
+ group :test do
10
+ gem 'rspec'
11
+ end
@@ -0,0 +1,25 @@
1
+ ---
2
+ dependencies:
3
+ rake:
4
+ group:
5
+ - :development
6
+ version: ">= 0"
7
+ rspec:
8
+ group:
9
+ - :test
10
+ version: ">= 0"
11
+ json:
12
+ group:
13
+ - :development
14
+ version: ">= 0"
15
+ specs:
16
+ - rake:
17
+ version: 0.8.7
18
+ - json:
19
+ version: 1.4.3
20
+ - rspec:
21
+ version: 1.3.0
22
+ hash: d9c314ac1790a9ac25dfdaf7574b86d62b88f1d5
23
+ sources:
24
+ - Rubygems:
25
+ uri: http://gemcutter.org
@@ -1,4 +1,4 @@
1
- Copyright (c) <%= Date.today.year %> <YOUR NAME HERE>
1
+ Copyright (c) 2009 Intridea, Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -0,0 +1,101 @@
1
+ = Hashie
2
+
3
+ Hashie is a growing collection of tools that extend Hashes and make
4
+ them more useful.
5
+
6
+ == Installation
7
+
8
+ Hashie is available as a RubyGem:
9
+
10
+ gem install hashie
11
+
12
+ == Mash
13
+
14
+ Mash is an extended Hash that gives simple pseudo-object functionality
15
+ that can be built from hashes and easily extended. It is designed to
16
+ be used in RESTful API libraries to provide easy object-like access
17
+ to JSON and XML parsed hashes.
18
+
19
+ === Example:
20
+
21
+ mash = Hashie::Mash.new
22
+ mash.name? # => false
23
+ mash.name # => nil
24
+ mash.name = "My Mash"
25
+ mash.name # => "My Mash"
26
+ mash.name? # => true
27
+ mash.inspect # => <Hashie::Mash name="My Mash">
28
+
29
+ mash = Mash.new
30
+ # use bang methods for multi-level assignment
31
+ mash.author!.name = "Michael Bleigh"
32
+ mash.author # => <Hashie::Mash name="Michael Bleigh">
33
+
34
+ == Dash
35
+
36
+ Dash is an extended Hash that has a discrete set of defined properties
37
+ and only those properties may be set on the hash. Additionally, you
38
+ can set defaults for each property.
39
+
40
+ === Example:
41
+
42
+ class Person < Hashie::Dash
43
+ property :name
44
+ property :email
45
+ property :occupation, :default => 'Rubyist'
46
+ end
47
+
48
+ p = Person.new
49
+ p.name # => nil
50
+ p.email = 'abc@def.com'
51
+ p.occupation # => 'Rubyist'
52
+ p.email # => 'abc@def.com'
53
+ p[:awesome] # => NoMethodError
54
+ p[:occupation] # => 'Rubyist'
55
+
56
+ p = Person.new(:name => "Bob")
57
+ p.name # => 'Bob'
58
+ p.occupation # => 'Rubyist'
59
+
60
+ == Clash
61
+
62
+ Clash is a Chainable Lazy Hash that allows you to easily construct
63
+ complex hashes using method notation chaining. This will allow you
64
+ to use a more action-oriented approach to building options hashes.
65
+
66
+ Essentially, a Clash is a generalized way to provide much of the same
67
+ kind of "chainability" that libraries like Arel or Rails 2.x's named_scopes
68
+ provide.
69
+
70
+ === Example
71
+
72
+ c = Hashie::Clash.new
73
+ c.where(:abc => 'def').order(:created_at)
74
+ c # => {:where => {:abc => 'def}, :order => :created_at}
75
+
76
+ # You can also use bang notation to chain into sub-hashes,
77
+ # jumping back up the chain with _end!
78
+ c = Hashie::Clash.new
79
+ c.where!.abc('def').ghi(123)._end!.order(:created_at)
80
+ c # => {:where => {:abc => 'def', :ghi => 123}, :order => :created_at}
81
+
82
+ # Multiple hashes are merged automatically
83
+ c = Hashie::Clash.new
84
+ c.where(:abc => 'def').where(:hgi => 123)
85
+ c # => {:where => {:abc => 'def', :hgi => 123}}
86
+
87
+ == Note on Patches/Pull Requests
88
+
89
+ * Fork the project.
90
+ * Make your feature addition or bug fix.
91
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
92
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
93
+ * Send me a pull request. Bonus points for topic branches.
94
+
95
+ == Authors
96
+
97
+ * Michael Bleigh
98
+
99
+ == Copyright
100
+
101
+ Copyright (c) 2009 Intridea, Inc (http://intridea.com/). See LICENSE for details.