orange 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/README.markdown +154 -0
  2. data/lib/orange.rb +7 -0
  3. data/lib/orange/application.rb +125 -0
  4. data/lib/orange/carton.rb +114 -0
  5. data/lib/orange/cartons/site_carton.rb +9 -0
  6. data/lib/orange/core.rb +197 -0
  7. data/lib/orange/magick.rb +91 -0
  8. data/lib/orange/middleware/access_control.rb +100 -0
  9. data/lib/orange/middleware/base.rb +41 -0
  10. data/lib/orange/middleware/database.rb +22 -0
  11. data/lib/orange/middleware/globals.rb +18 -0
  12. data/lib/orange/middleware/recapture.rb +19 -0
  13. data/lib/orange/middleware/rerouter.rb +13 -0
  14. data/lib/orange/middleware/restful_router.rb +59 -0
  15. data/lib/orange/middleware/route_context.rb +39 -0
  16. data/lib/orange/middleware/route_site.rb +51 -0
  17. data/lib/orange/middleware/show_exceptions.rb +78 -0
  18. data/lib/orange/middleware/site_load.rb +33 -0
  19. data/lib/orange/middleware/static.rb +81 -0
  20. data/lib/orange/middleware/static_file.rb +32 -0
  21. data/lib/orange/middleware/template.rb +61 -0
  22. data/lib/orange/model_resource.rb +170 -0
  23. data/lib/orange/packet.rb +88 -0
  24. data/lib/orange/resource.rb +37 -0
  25. data/lib/orange/resources/flex_router.rb +13 -0
  26. data/lib/orange/resources/mapper.rb +55 -0
  27. data/lib/orange/resources/page_parts.rb +54 -0
  28. data/lib/orange/resources/parser.rb +60 -0
  29. data/lib/orange/routable_resource.rb +16 -0
  30. data/lib/orange/stack.rb +201 -0
  31. data/spec/application_spec.rb +146 -0
  32. data/spec/carton_spec.rb +5 -0
  33. data/spec/core_spec.rb +231 -0
  34. data/spec/magick_spec.rb +89 -0
  35. data/spec/mock/mock_app.rb +17 -0
  36. data/spec/mock/mock_core.rb +2 -0
  37. data/spec/mock/mock_middleware.rb +13 -0
  38. data/spec/mock/mock_mixins.rb +19 -0
  39. data/spec/mock/mock_pulp.rb +24 -0
  40. data/spec/mock/mock_resource.rb +5 -0
  41. data/spec/mock/mock_router.rb +10 -0
  42. data/spec/model_resource_spec.rb +5 -0
  43. data/spec/orange_spec.rb +19 -0
  44. data/spec/packet_spec.rb +134 -0
  45. data/spec/resource_spec.rb +5 -0
  46. data/spec/resources/flex_router_spec.rb +5 -0
  47. data/spec/resources/mapper_spec.rb +5 -0
  48. data/spec/resources/parser_spec.rb +5 -0
  49. data/spec/routable_resource_spec.rb +5 -0
  50. data/spec/spec_helper.rb +16 -0
  51. data/spec/stack_spec.rb +202 -0
  52. data/spec/stats.rb +182 -0
  53. metadata +194 -0
@@ -0,0 +1,154 @@
1
+ Orange
2
+ ======
3
+
4
+ Orange is intended to be a middle ground between the simplicity of Sinatra
5
+ and the power of Rails. Orange is being developed by Orange Sparkle Ball, inc
6
+ for our own use. Our main focus is on creating a super-extensible CMS
7
+ with Orange, but we're trying to make the components as reusable as possible. Our
8
+ intention is to be ready to use Orange for most client website builds by
9
+ March 2010.
10
+
11
+ **Note**: Orange is still in the alpha stage. Test coverage is lack-luster at best.
12
+ Tread carefully.
13
+
14
+ A (Theoretical) Example of Orange
15
+ =================================
16
+
17
+ _This doesn't actually work quite yet, but it's the goal we're working toward._
18
+
19
+ After installing the orange gem, create an 'app.rb'
20
+
21
+ **app.rb:**
22
+
23
+ require 'rubygems'
24
+ require 'orange'
25
+ class App < Orange::Application
26
+ end
27
+
28
+ You now have an Orange CMS that can be made by calling "App.app".
29
+ Put this line in your rackup file...
30
+
31
+ **config.ru:**
32
+
33
+ require 'app'
34
+ run App.app
35
+
36
+ Run rack however you run rack.
37
+
38
+ Look at that, a full fledged CMS in 6 lines! Not so impressive, it's all prebuilt,
39
+ right? The real question is how hard is it to customize?
40
+
41
+ I want my pages to have more than just titles and bodies. I want sidebars...
42
+
43
+ **app.rb:**
44
+
45
+ require 'rubygems'
46
+ require 'orange'
47
+ class App < Orange::Application
48
+ end
49
+ class Orange::Page
50
+ markdown :sidebar, :context => [:front]
51
+ end
52
+
53
+ We now have a sidebar that anybody can see. The backend scaffolding will adapt to allow
54
+ editing, and the front end will print it out for each page. Slap some CSS on it to make it
55
+ look like a sidebar, and tada!
56
+
57
+ Pages now have sidebars, in three lines of code and some
58
+ styling. No migrations (we rely on DataMapper's auto_upgrade functionality), no extra
59
+ files (unless we want them).
60
+
61
+ More Info
62
+ =========
63
+
64
+ Orange Philosophy
65
+ -----------------
66
+ The Orange application framework is intended to be a fully customizable CMS
67
+ capable of hosting multiple sites while maintaining Sinatra-like ease of
68
+ programming. Some core ideas behind Orange:
69
+
70
+ * Scaffolding doesn't have to be replaced if it's smart enough (most of the time)
71
+ * Put as much functionality into middleware as possible, so it can be easily reused
72
+ and remixed
73
+ * Give middleware a little more power so it's useful enough to handle more tasks
74
+
75
+
76
+ Should I Use Orange?
77
+ --------------------
78
+ Not right now, unless you want to write half the framework yourself.
79
+
80
+
81
+ When it's finished, would I want to use it?
82
+ -------------------------------------------
83
+ Depends on what you're looking for. Orange has a middleware stack intended to
84
+ be reused. If the stack has something you'd like, you could theoretically
85
+ put the middleware stack on top of Sinatra or Rails. (This hasn't actually
86
+ been tested yet.)
87
+
88
+ The full Orange application framework is intended to run
89
+ as an easily extensible CMS. We tend to think that having lots of tests
90
+ and full MVC separation just so you can add an extra type of page to the CMS
91
+ is a bit overkill. We designed this to replace ModX in our web builds for clients.
92
+
93
+ Required Gems
94
+ -------------
95
+
96
+ Make sure githubs gems can be downloaded:
97
+
98
+ $ gem sources -a http://gems.github.com
99
+
100
+ * dm-core (+ do_[sqlite3|mysql|...] )
101
+ * dm-more
102
+ * rack
103
+ * haml
104
+ * mynyml-rack-abstract-format (github)
105
+ * ruby-openid
106
+ * rack-openid
107
+ * meekish-openid_dm_store
108
+
109
+ Also, you'll need a web server of some kind and need to set it up for rack.
110
+
111
+ **Testing**
112
+
113
+ If you want to test, you'll need the following gems:
114
+
115
+ * rspec
116
+ * rack-test
117
+
118
+ Yard is also helpful for generating API docs
119
+
120
+ The following are useful rake tasks for testing purposes:
121
+
122
+ * rake test => (same as rake spec)
123
+ * rake spec => runs rspec with color enabled and spec_helper included
124
+ * rake doc => runs yardoc (no, not really necessary)
125
+ * rake clean => clear out the temporary files not included in the repo
126
+ * rake rcov => runs rspec with rcov
127
+
128
+ Programming Info
129
+ ================
130
+
131
+ The basics of using the orange framework...
132
+
133
+ Terminology
134
+ -----------
135
+
136
+ * **Application**: The last stop for the packet after traversing through the middleware stack.
137
+ * **Core**: This is the core orange object, accessible from all points of the orange
138
+ system. Usually the orange instance can be called by simply using the "orange" function
139
+ * **Mixins**: Extra functionality added directly to the core. Mixins are generally for only
140
+ a couple of extra methods, anything more should probably be created as a resource.
141
+ * **Packet**: This object represents a web request coming in to the orange system.
142
+ Each request is instantiated as a packet before it is sent through the middleware stack.
143
+ * **Pulp**: Mixin added to the packet object rather than the Core.
144
+ * **Resources**: Resources are extra functionality contained within an object, accessible
145
+ from the core.
146
+ * **Stack**: The bundled collection of Orange-enhanced middleware sitting on top of the
147
+ Orange application
148
+
149
+ Pulp and Mixins
150
+ ---------------
151
+ The ability to add pulp and mixins is incredibly handy because the packet and the core are
152
+ available from just about anywhere in the Orange framework. For instance, the haml parser
153
+ evaluates all local calls as if made to the packet, so adding pulp is essentially adding
154
+ functionality that is directly available to haml.
@@ -0,0 +1,7 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ Dir.glob(File.join(libdir, 'orange', '*.rb')).each {|f| require f }
5
+ Dir.glob(File.join(libdir, 'orange', 'cartons', '*.rb')).each {|f| require f }
6
+ Dir.glob(File.join(libdir, 'orange', 'middleware', '*.rb')).each {|f| require f }
7
+ Dir.glob(File.join(libdir, 'orange', 'resources', '*.rb')).each {|f| require f }
@@ -0,0 +1,125 @@
1
+ module Orange
2
+ # Orange::Application is the main class used for building an
3
+ # Orange App. Typically you will not initialize it directly,
4
+ # but use the app method, which returns the entire Orange
5
+ # stack, with all middleware and the Orange::Application as
6
+ # the main receiver
7
+ #
8
+ # To override the stack generated by default, you can use
9
+ # the self.stack method, which will be used to create a new
10
+ # Orange::Stack
11
+ class Application
12
+ # Initialize will set the core, and additionally accept any
13
+ # other options to be added in to the opts array
14
+ # @param [Orange::Core] core the orange core instance that this application will use
15
+ # @param [Hash] *opts the optional arguments
16
+ def initialize(core = false, *opts, &block)
17
+ @core = core
18
+ @options ||= {}
19
+ @options = Orange::Options.new(*opts, &block).hash.with_defaults(self.class.opts)
20
+ orange.register(:stack_loaded) do |s|
21
+ stack_init
22
+ end
23
+ init
24
+ end
25
+
26
+ # stack_init is ONLY called after the {#app}
27
+ # method is called, loading a stack
28
+ # (just in case the middleware stack added necessary functionality, etc)
29
+ def stack_init
30
+ end
31
+
32
+ # This method is called by initialize, subclasses should override this method
33
+ # for their own initialization needs.
34
+ #
35
+ # It will usually be better to use stack_init, which gives full access to
36
+ # the initialized stack
37
+ def init
38
+ end
39
+
40
+ # Set the orange core to be a new core
41
+ #
42
+ # Generally, the core should be set during initialization, rather
43
+ # than with this method.
44
+ # @param [Orange::Core] core the orange core instance
45
+ def set_core(core)
46
+ @core = core
47
+ end
48
+
49
+ # The standard call as required by rack. This will
50
+ # make an Orange::Packet object (if necessary) and
51
+ # then send it to the appropriate router for routing.
52
+ #
53
+ # If the :self_routing option is true (default) then
54
+ # the packet will be routed by the application if there
55
+ # is not already another class volunteering for that role.
56
+ # (Routers declare themselves in the orange
57
+ # env['route.router'] to be called by the application)
58
+ #
59
+ #
60
+ def call(env)
61
+ packet = Orange::Packet.new(@core, env)
62
+ # Set up this application as router if nothing else has
63
+ # assumed routing responsibility (for Sinatra DSL like routing)
64
+ self_routing = opts[:self_routing] || true
65
+ if (!packet['route.router'] && self_routing)
66
+ packet['route.router'] = self
67
+ end
68
+ packet.route
69
+ packet.finish
70
+ end
71
+
72
+ # Returns the core
73
+ # @return [Orange::Core] the core instance set for the application
74
+ def orange
75
+ @core
76
+ end
77
+
78
+ # The default route method for the application. Must be overridden in subclasses.
79
+ #
80
+ # This method will raise a RuntimeError if not overridden.
81
+ # The intent is for the application subclass to override this method
82
+ # and use it to handle packets not routed by Stack middleware.
83
+ def route(packet)
84
+ raise 'default response from Orange::Application.route'
85
+ end
86
+
87
+ # Used to set optional values at class level. Will be merged into the options
88
+ # given at initialization time
89
+ def self.set(key, v = true)
90
+ @class_opts ||= {}
91
+ @class_opts[key] = v
92
+ end
93
+
94
+ # Gives access to class defined options.
95
+ def self.opts
96
+ @class_opts ||= {}
97
+ end
98
+
99
+ # Gives access to options for the application, both from the class and the
100
+ # instance level.
101
+ # @return [Hash] the options hash
102
+ def opts
103
+ @options
104
+ end
105
+
106
+ # Returns an instance of Orange::Stack to be run by Rack
107
+ #
108
+ # Usually, you'll call this in the rackup file: `run MyApplication.app`
109
+ def self.app
110
+ if @app.instance_of?(Proc)
111
+ Orange::Stack.new &@app # turn saved proc into a block arg
112
+ else
113
+ Orange::Stack.new self
114
+ end
115
+ end
116
+
117
+ # Changes the stack that will be used when {#app}
118
+ # is called
119
+ #
120
+ # Each call to stack overrides the previous one.
121
+ def self.stack(&block)
122
+ @app = Proc.new # pulls in the block and makes it a proc
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,114 @@
1
+ require 'dm-core'
2
+
3
+ module Orange
4
+ class Carton
5
+
6
+ def self.as_resource
7
+ name = self.to_s
8
+ eval <<-HEREDOC
9
+ class ::#{name}_Resource < Orange::ModelResource
10
+ use #{name}
11
+ end
12
+ HEREDOC
13
+ end
14
+
15
+ # Info for
16
+ include DataMapper::Types
17
+
18
+ def self.id
19
+ include DataMapper::Resource
20
+ self.property(:id, Serial)
21
+ @scaffold_properties = []
22
+ init
23
+ end
24
+
25
+ def self.init
26
+ end
27
+
28
+ # Return properties that should be shown for a given context
29
+ def self.form_props(context)
30
+ @scaffold_properties.select{|p| p[:levels].include?(context) }
31
+ end
32
+
33
+ # Helper to wrap properties into admin level
34
+ def self.admin(&block)
35
+ @levels = [:admin, :orange]
36
+ instance_eval(&block)
37
+ @levels = false
38
+ end
39
+
40
+ # Helper to wrap properties into orange level
41
+ def self.orange(&block)
42
+ @levels = [:orange]
43
+ instance_eval(&block)
44
+ @levels = false
45
+ end
46
+
47
+ # Helper to wrap properties into front level
48
+ def self.front(&block)
49
+ @levels = [:live, :admin, :orange]
50
+ instance_eval(&block)
51
+ @levels = false
52
+ end
53
+
54
+ # Define a helper for title type database stuff
55
+ # Show in a context if wrapped in one of the helpers
56
+ def self.title(name, opts = {})
57
+ @scaffold_properties << {:name => name, :type => :title, :levels => @levels}.merge(opts) if @levels
58
+ self.property(name, String, opts)
59
+ end
60
+
61
+ # Define a helper for fulltext type database stuff
62
+ # Show in a context if wrapped in one of the helpers
63
+ def self.fulltext(name, opts = {})
64
+ @scaffold_properties << {:name => name, :type => :fulltext, :levels => @levels, :opts => opts} if @levels
65
+ self.property(name, Text, opts)
66
+ end
67
+
68
+ # Define a helper for input type="text" type database stuff
69
+ # Show in a context if wrapped in one of the helpers
70
+ def self.text(name, opts = {})
71
+ @scaffold_properties << {:name => name, :type => :text, :levels => @levels, :opts => opts} if @levels
72
+ self.property(name, String, opts)
73
+ end
74
+
75
+ # Define a helper for input type="text" type database stuff
76
+ # Show in a context if wrapped in one of the helpers
77
+ def self.string(name, opts = {})
78
+ self.text(name, opts)
79
+ end
80
+
81
+ # Override DataMapper to include context sensitivity (as set by helpers)
82
+ def self.property(name, type, opts = {})
83
+ my_type = type.to_s.downcase.to_sym
84
+ @scaffold_properties << {:name => name, :type => my_type, :levels => @levels}.merge(opts) if @levels
85
+ property(name, type, opts)
86
+ end
87
+
88
+
89
+ # For more generic cases, use same syntax as DataMapper
90
+ # This will make it an admin property though.
91
+ def self.admin_property(name, type, opts = {})
92
+ my_type = type.to_s.downcase.to_sym
93
+ @scaffold_properties << {:name => name, :type => my_type, :levels => [:admin, :orange]}.merge(opts)
94
+ property(name, type, opts)
95
+ end
96
+
97
+ # For more generic cases, use same syntax as DataMapper
98
+ # This will make it a front property though.
99
+ def self.front_property(name, type, opts = {})
100
+ my_type = type.to_s.downcase.to_sym
101
+ @scaffold_properties << {:name => name, :type => my_type, :levels => [:live, :admin, :orange]}.merge(opts)
102
+ property(name, type, opts)
103
+ end
104
+
105
+ # For more generic cases, use same syntax as DataMapper
106
+ # This will make it an orange property though.
107
+ def self.orange_property(name, type, opts = {})
108
+ my_type = type.to_s.downcase.to_sym
109
+ @scaffold_properties << {:name => name, :type => my_type, :levels => [:orange]}.merge(opts)
110
+ property(name, type, opts)
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,9 @@
1
+ require 'dm-core'
2
+
3
+ module Orange
4
+ class SiteCarton < Carton
5
+ def self.init
6
+ belongs_to :orange_site, 'Orange::Site'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,197 @@
1
+ require 'dm-core'
2
+ require 'rack'
3
+ require 'rack/builder'
4
+
5
+ module Orange
6
+ # Declare submodules for later use
7
+ module Pulp; end
8
+ module Mixins; end
9
+
10
+ # Allow mixins directly from Orange
11
+ def self.mixin(inc)
12
+ Core.mixin inc
13
+ end
14
+
15
+ # Allow pulp directly from Orange
16
+ def self.add_pulp(inc)
17
+ Packet.mixin inc
18
+ end
19
+
20
+ # Core is one of two main sources of interaction for Orange Applications
21
+ #
22
+ # All portions of Orange based code have access to the Core upon
23
+ # initialization. Orange allows access to individual resources,
24
+ # and also allows single point for event registration and firing.
25
+ #
26
+ # Functionality of the core can be extended by loading resources,
27
+ # or by mixins that directly affect the Core. Generally, resources
28
+ # are the less convoluted (easier to debug) way to do it.
29
+ class Core
30
+ # Sets the default options for Orange Applications
31
+ DEFAULT_CORE_OPTIONS =
32
+ {
33
+ :contexts => [:live, :admin, :orange],
34
+ :default_context => :live,
35
+ :default_resource => :not_found,
36
+ :default_database => 'sqlite3::memory:'
37
+ } unless defined?(DEFAULT_CORE_OPTIONS)
38
+
39
+ # Args will be set to the @options array.
40
+ # Block DSL style option setting also available:
41
+ #
42
+ # orange = Orange::Core.new(:optional_option => 'foo') do
43
+ # haml true
44
+ # site_name "Banana"
45
+ # custom_router MyRouterClass.new
46
+ # end
47
+ #
48
+ # orange.options[:site_name] #=> "Banana"
49
+ #
50
+ # This method calls afterLoad when it is done. Subclasses can override
51
+ # the afterLoad method for initialization needs.
52
+ def initialize(*args, &block)
53
+ @options = Options.new(*args, &block).hash.with_defaults(DEFAULT_CORE_OPTIONS)
54
+ @resources = {}
55
+ @events = {}
56
+ @file = __FILE__
57
+ load(Orange::Parser.new, :parser)
58
+ load(Orange::Mapper.new, :mapper)
59
+ load(Orange::PageParts.new, :page_parts)
60
+ afterLoad
61
+ self
62
+ end
63
+
64
+ # Returns the orange library directory
65
+ # @return [String] the directory name indicating where the core file is
66
+ # located
67
+ def core_dir
68
+ options[:core_dir] ||= File.dirname(__FILE__)
69
+ end
70
+
71
+ # Returns the directory of the currently executing file (using Dir.pwd),
72
+ # can be overriden using the option :app_dir in initialization
73
+ #
74
+ # @return [String] the directory name of the currently running application
75
+ def app_dir
76
+ options[:app_dir] ||= Dir.pwd
77
+ end
78
+
79
+ # Called by initialize after finished loading
80
+ def afterLoad
81
+ true
82
+ end
83
+
84
+ # Returns status of a given resource by short name
85
+ # @param [Symbol] resource_name The short name of the resource
86
+ # @return [Boolean] result of has_key? in the resources list
87
+ def loaded?(resource_name)
88
+ @resources.has_key?(resource_name)
89
+ end
90
+
91
+ # Takes an instance of a Orange::Resource subclass, sets orange
92
+ # then adds it to the orange resources
93
+ #
94
+ # It can be assigned a short name to be used for accessing later
95
+ # on. If no short name is assigned, one will be generated by downcasing
96
+ # the class name and changing it to a symbol
97
+ #
98
+ # Resources must respond to set_orange, which is automatically used to
99
+ # create a link back to the Core, and to notify the resource of its assigned
100
+ # short name.
101
+ #
102
+ # @param [Orange::Resource] resource An instance of Orange::Resource subclass
103
+ # @param [optional, Symbol, String] name A short name to assign as key in Hash
104
+ # list of resources.
105
+ # Doesn't necessarily need to be a symbol, but generally is.
106
+ # Set to the class name lowercase as a symbol by default.
107
+ def load(resource, name = false)
108
+ name = resource.class.to_s.gsub(/::/, '_').downcase.to_sym if(!name)
109
+ @resources[name] = resource.set_orange(self, name)
110
+ end
111
+
112
+ # Convenience self for consistent naming across middleware
113
+ # @return [Orange::Core] self
114
+ def orange; self; end
115
+
116
+ # Registers interest in a callback for a named event.
117
+ #
118
+ # Event registration is stored as a hash list of events and arrays
119
+ # of procs to be executed on each event.
120
+ #
121
+ # @param [Symbol] event the name of the event registered for
122
+ # @param [optional, Integer] position the position to place the event in,
123
+ # by default goes to the front of the list. Doesn't necessarily need
124
+ # to be exact count, empty spaces in array are taken out. Forcing the
125
+ # event to be at 99 or some such position will typically make sure it
126
+ # happens last in the firing process.
127
+ # @param [Block] block The code to be executed upon event firing.
128
+ # Saved to an array of procs that are called when #fire is called.
129
+ # Block must accept one param, which is the intended to be the packet
130
+ # causing the block to fire, unless the event happens in setup.
131
+ def register(event, position = 0, &block)
132
+ if block_given?
133
+ if @events[event]
134
+ @events[event].insert(position, Proc.new)
135
+ else
136
+ @events[event] = Array.new.insert(position, Proc.new)
137
+ end
138
+ end
139
+ end
140
+
141
+ # Fires a callback for a given packet (or other object)
142
+ #
143
+ # @param [Symbol] event name of event something has registered for
144
+ # @param [Orange::Packet, object] packet Object, generally Orange::Packet,
145
+ # causing the fire. This is passed to each Proc registered.
146
+ # @return [Boolean] returns false if nothing has been registered for the
147
+ # event, otherwise true.
148
+ def fire(event, packet)
149
+ return false unless @events[event]
150
+ @events[event].compact!
151
+ for callback in @events[event]
152
+ callback.call(packet)
153
+ end
154
+ true
155
+ end
156
+
157
+ # Returns options of the orange core
158
+ #
159
+ # @return [Hash] Hash of options
160
+ def options
161
+ @options
162
+ end
163
+
164
+
165
+ # Accesses resources array, stored as a hash {:short_name => Resource instance,...}
166
+ #
167
+ # @param [Symbol] name the short name for the requested resource
168
+ # @return [Orange::Resource] the resource for the given short name
169
+ def [](name)
170
+ @resources[name]
171
+ end
172
+
173
+ # Includes module in the Packet class
174
+ # @param [Module] inc module to be included
175
+ def add_pulp(inc)
176
+ self.class.add_pulp inc
177
+ end
178
+
179
+ # Includes module in this class
180
+ # @param [Module] inc module to be included
181
+ def mixin(inc)
182
+ self.class.mixin inc
183
+ end
184
+
185
+ # Includes module in this class
186
+ # @param [Module] inc module to be included
187
+ def self.mixin(inc)
188
+ include inc
189
+ end
190
+
191
+ # Includes module in the Packet class
192
+ # @param [Module] inc module to be included
193
+ def self.add_pulp(inc)
194
+ Packet.mixin inc
195
+ end
196
+ end
197
+ end