cookie_cutter 0.1.0

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/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ #exclude rubymine ide files
20
+ .idea/
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby-19mode # JRuby in 1.9 mode
6
+ - rbx-19mode
7
+ # uncomment this line if your project needs to run something other than `rake`:
8
+ # script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ # Specify your gem's dependencies in cookie_cutter.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Andy Alm
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # CookieCutter
2
+
3
+ WARNING: CookieCutter is very early in its development cycle (pre-alpha).
4
+
5
+ CookieCutter provides a nice DSL for defining your cookies so that you can formalize your cookie definitions.
6
+ This leads to DRY-er code by ensuring that concerns such as the domain and lifetime of a cookie are consistent.
7
+ It also makes it easy to know what info you are putting into your cookies, which is becoming more important
8
+ these days as more laws surrounding cookies get written.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'cookie_cutter'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install cookie_cutter
23
+
24
+ ## Usage Examples
25
+
26
+ ### Define your cookie
27
+
28
+ class MyCookie < CookieCutter::Base
29
+ store_as :my
30
+ domain :all
31
+ is_permanent
32
+ secure_requests_only
33
+ http_only
34
+ has_attribute :language
35
+ has_attribute :country
36
+ end
37
+
38
+ ### Use your cookie
39
+
40
+ #writes your cookie
41
+ cookie = MyCookie.find(request)
42
+ cookie.language = "fr"
43
+
44
+ #reads your cookie
45
+ cookie = MyCookie.find(request)
46
+ puts "My language is #{cookie.language}"
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env rake
2
+
3
+ #use bundler to resolve dependencies
4
+ require 'bundler'
5
+ Bundler.require(:default, :development, :test)
6
+
7
+ #import standard bundler gem tasks
8
+ require "bundler/gem_tasks"
9
+
10
+ #import/define rspec task
11
+ require 'rspec/core/rake_task'
12
+ RSpec::Core::RakeTask.new(:spec)
13
+
14
+ desc 'Runs all the tests'
15
+ task :tests => :spec
16
+ task :t => :tests
17
+
18
+ task :default => [:tests,:build]
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/cookie_cutter/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Andy Alm"]
6
+ gem.email = ["private"]
7
+ gem.description = %q{Provides a way to define the structure, lifetime, and other properties of a cookie all in one place.}
8
+ gem.summary = gem.description
9
+ gem.homepage = "http://github.com/andyalm/cookie_cutter"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "cookie_cutter"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = CookieCutter::VERSION
17
+
18
+ gem.add_development_dependency 'activesupport' #for date helpers
19
+ gem.add_development_dependency 'rake'
20
+ gem.add_development_dependency 'rspec'
21
+ end
@@ -0,0 +1,7 @@
1
+ require 'cookie_cutter/cookie'
2
+
3
+ module CookieCutter
4
+ class Base
5
+ include Cookie
6
+ end
7
+ end
@@ -0,0 +1,140 @@
1
+ module CookieCutter
2
+ module Cookie
3
+ module ClassMethods
4
+ def find(request)
5
+ new(request.cookie_jar)
6
+ end
7
+
8
+ attr_reader :cookie_name
9
+ def store_as(name)
10
+ @cookie_name = name
11
+ end
12
+
13
+ attr_reader :cookie_domain
14
+ def domain(domain_value)
15
+ @cookie_domain = domain_value
16
+ add_handler do |cookie|
17
+ cookie[:domain] = domain_value
18
+ end
19
+ end
20
+
21
+ attr_reader :cookie_lifetime
22
+ def lifetime(seconds)
23
+ @cookie_lifetime = seconds
24
+ add_handler do |cookie|
25
+ cookie[:expires] = (Time.now + seconds)
26
+ end
27
+ end
28
+
29
+ def is_permanent
30
+ twenty_years = 60 * 60 * 24 * 365.25 * 20
31
+ lifetime twenty_years
32
+ end
33
+
34
+ def secure_requests_only
35
+ @secure = true
36
+ add_handler do |cookie|
37
+ cookie[:secure] = true
38
+ end
39
+ end
40
+
41
+ def secure?
42
+ @secure ? true : false
43
+ end
44
+
45
+ def http_only
46
+ @http_only = true
47
+ add_handler do |cookie|
48
+ cookie[:httponly] = true
49
+ end
50
+ end
51
+ alias_method :httponly, :http_only
52
+
53
+ def http_only?
54
+ @http_only ? true : false
55
+ end
56
+ alias_method :httponly?, :http_only?
57
+
58
+ def has_attribute(value_name, options={})
59
+ raise "CookieCutter value names must by symbols. #{value_name} is not a symbol" unless value_name.is_a?(Symbol)
60
+ #make value and value= private when the cookie has one or more named values
61
+ private :value, :value=, :set_value
62
+
63
+ value_key = (options[:store_as] || value_name).to_sym
64
+ send :define_method, value_name do
65
+ get_attribute_value(value_key)
66
+ end
67
+ setter_method_name = "#{value_name.to_s}=".to_sym
68
+ send :define_method, setter_method_name do |value|
69
+ set_attribute_value(value_key, value)
70
+ end
71
+ end
72
+
73
+ def add_options(cookie)
74
+ handlers.each do |handler|
75
+ handler.call(cookie)
76
+ end
77
+ end
78
+
79
+ def add_handler(&block)
80
+ handlers << block
81
+ end
82
+
83
+ def handlers
84
+ @handlers ||= []
85
+ end
86
+ end
87
+
88
+ def self.included(klass)
89
+ klass.extend ClassMethods
90
+ end
91
+
92
+ def initialize(cookie_jar)
93
+ @cookie_jar = cookie_jar
94
+ end
95
+
96
+ def value
97
+ @cookie_jar[cookie_name]
98
+ end
99
+
100
+ def value=(val)
101
+ cookie = { value: val }
102
+ self.class.add_options(cookie)
103
+ @cookie_jar[cookie_name] = cookie
104
+ end
105
+
106
+ def delete!
107
+ @cookie_jar.delete(cookie_name)
108
+ end
109
+
110
+ def cookie_name
111
+ self.class.cookie_name
112
+ end
113
+
114
+ def secure?
115
+ self.class.secure?
116
+ end
117
+
118
+ def cookie_lifetime
119
+ self.class.cookie_lifetime
120
+ end
121
+
122
+ def cookie_domain
123
+ self.class.cookie_domain
124
+ end
125
+
126
+ alias_method :set_value, :value=
127
+
128
+ private
129
+ def set_attribute_value(value_name, val)
130
+ values_hash = value() || {}
131
+ values_hash[value_name] = val
132
+ set_value(values_hash)
133
+ end
134
+
135
+ def get_attribute_value(value_name)
136
+ values_hash = value() || {}
137
+ values_hash[value_name]
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,3 @@
1
+ module CookieCutter
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "cookie_cutter/version"
2
+ require "cookie_cutter/base"
3
+
4
+ module CookieCutter
5
+ # Your code goes here...
6
+ end
data/spec/base_spec.rb ADDED
@@ -0,0 +1,158 @@
1
+ require 'rspec'
2
+ require 'cookie_cutter'
3
+ require_relative 'support/fake_cookie_jar'
4
+ require 'active_support/core_ext'
5
+
6
+ class SingleValuedCookie < CookieCutter::Base
7
+ store_as :svc
8
+ end
9
+
10
+ class MultiValuedCookie < CookieCutter::Base
11
+ store_as :mvc
12
+
13
+ has_attribute :value1
14
+ has_attribute :value2, store_as: 'val2'
15
+ end
16
+
17
+ class MyMixedCaseCookie < CookieCutter::Base
18
+ store_as :my_uppercase_cookie
19
+
20
+ has_attribute :mixed_case_key, store_as: 'mixedcasekey'
21
+ end
22
+
23
+ describe CookieCutter::Base do
24
+ let(:cookie_jar) { FakeCookieJar.new }
25
+ it 'should not not update the cookie_jar when no value is set' do
26
+ SingleValuedCookie.new(cookie_jar)
27
+ cookie_jar.to_hash.should be_empty
28
+ end
29
+ describe 'domain' do
30
+ it 'does not set domain if not given' do
31
+ class CookieWithNoDomain < CookieCutter::Base
32
+ store_as :cwnd
33
+ end
34
+ cookie = CookieWithNoDomain.new(cookie_jar)
35
+ cookie.value = "my value"
36
+ cookie_jar.metadata_for(:cwnd)[:domain].should be_nil
37
+ end
38
+ it 'uses given domain when saving cookie' do
39
+ class CookieWithDomain < CookieCutter::Base
40
+ store_as :cwd
41
+ domain :all
42
+ end
43
+ cookie = CookieWithDomain.new(cookie_jar)
44
+ cookie.value = "my value"
45
+ cookie_jar.metadata_for(:cwd)[:domain].should == :all
46
+ end
47
+ end
48
+ describe 'lifetime' do
49
+ it 'defaults to a session cookie' do
50
+ class CookieWithNoLifetimeSpecified < CookieCutter::Base
51
+ store_as :cwnls
52
+ end
53
+ cookie = CookieWithNoLifetimeSpecified.new(cookie_jar)
54
+ cookie.value = "my value"
55
+ cookie_jar.metadata_for(:cwnls)[:expires].should be_nil
56
+ end
57
+ it 'sets expires to now plus lifetime' do
58
+ now = Time.now
59
+ lifetime = 60
60
+ class CookieWithLifetimeSpecified < CookieCutter::Base
61
+ store_as :cwls
62
+
63
+ lifetime 60
64
+ end
65
+ cookie = CookieWithLifetimeSpecified.new(cookie_jar)
66
+ cookie.value = "my value"
67
+ cookie_jar.metadata_for(:cwls)[:expires].should be_within(0.01).of(now + lifetime)
68
+ end
69
+ it 'sets expires to 20 years from now if made permanent!' do
70
+ now = Time.now
71
+ class PermanentCookie < CookieCutter::Base
72
+ store_as :pc
73
+ is_permanent
74
+ end
75
+ cookie = PermanentCookie.new(cookie_jar)
76
+ cookie.value = "my value"
77
+ cookie_jar.metadata_for(:pc)[:expires].should be_within(0.01).of(20.years.from_now)
78
+ end
79
+ end
80
+ describe 'secure_requests_only' do
81
+ it 'defaults to all requests (insecure) cookie' do
82
+ class CookieWithNoSecuritySpecified < CookieCutter::Base
83
+ store_as :cwnss
84
+ end
85
+ cookie = CookieWithNoSecuritySpecified.new(cookie_jar)
86
+ cookie.value = "my value"
87
+ cookie_jar.metadata_for(:cwnss)[:secure].should be_nil
88
+ end
89
+ it 'sets secure flag when secure_requests_only is specified' do
90
+ class CookieWithSecureRequestsOnly < CookieCutter::Base
91
+ store_as :cwsro
92
+ secure_requests_only
93
+ end
94
+ cookie = CookieWithSecureRequestsOnly.new(cookie_jar)
95
+ cookie.value = "my value"
96
+ cookie_jar.metadata_for(:cwsro)[:secure].should be_true
97
+ end
98
+ end
99
+ describe 'http_only' do
100
+ it 'defaults to being accessible to client scripts (i.e. not http_only)' do
101
+ class ClassWithNoHttpOnly < CookieCutter::Base
102
+ store_as :cwnho
103
+ end
104
+ cookie = ClassWithNoHttpOnly.new(cookie_jar)
105
+ cookie.value = "my value"
106
+ cookie_jar.metadata_for(:cwnho)[:httponly].should be_nil
107
+ end
108
+ it 'sets httponly flag when http_only is specified' do
109
+ class CookieWithHttpOnly < CookieCutter::Base
110
+ store_as :cwho
111
+ http_only
112
+ end
113
+ cookie = CookieWithHttpOnly.new(cookie_jar)
114
+ cookie.value = "my value"
115
+ cookie_jar.metadata_for(:cwho)[:httponly].should be_true
116
+ end
117
+ end
118
+ describe 'delete!' do
119
+ it 'should delete the cookie from the cookie jar' do
120
+ cookie = SingleValuedCookie.new(cookie_jar)
121
+ cookie.delete!
122
+ cookie_jar.deleted?(cookie.cookie_name).should be_true
123
+ end
124
+ end
125
+ describe 'single valued cookie' do
126
+ let(:single_valued_cookie) { SingleValuedCookie.new(cookie_jar) }
127
+ it 'should update the cookie jar when value is updated' do
128
+ single_valued_cookie.value = "ordinary value"
129
+ cookie_jar[:svc].should == "ordinary value"
130
+ end
131
+ it 'can be read via ordinary cookie jar' do
132
+ single_value_cookie = SingleValuedCookie.new(FakeCookieJar.new({ svc: "preset value" }))
133
+ single_value_cookie.value.should == "preset value"
134
+ end
135
+ end
136
+ describe 'multi-valued cookie' do
137
+ let(:multi_valued_cookie) { MultiValuedCookie.new(cookie_jar) }
138
+ it 'should update the cookie jar when an attribute is updated' do
139
+ multi_valued_cookie.value1 = "myval"
140
+ cookie_jar[:mvc][:value1].should == "myval"
141
+ end
142
+ it 'generates getters and setters for each attribute' do
143
+ multi_valued_cookie.value1 = "myval1"
144
+ multi_valued_cookie.value2 = "myval2"
145
+
146
+ multi_valued_cookie.value1.should == "myval1"
147
+ multi_valued_cookie.value2.should == "myval2"
148
+ end
149
+ it "privatizes the singular 'value' getter and setter" do
150
+ expect { multi_valued_cookie.value = "myval"}.should raise_error(NoMethodError)
151
+ expect { multi_valued_cookie.value}.should raise_error(NoMethodError)
152
+ end
153
+ it "can override stored attribute name with :store_as option" do
154
+ multi_valued_cookie.value2 = "myval2"
155
+ cookie_jar[:mvc][:val2].should == "myval2"
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,41 @@
1
+ class FakeCookieJar
2
+ def [](name)
3
+ cookie = @cookies[name]
4
+ if cookie
5
+ cookie[:value]
6
+ else
7
+ nil
8
+ end
9
+ end
10
+
11
+ def []=(name, value)
12
+ @cookies[name] = value
13
+ end
14
+
15
+ def delete(name)
16
+ @deleted_cookies << name
17
+ end
18
+
19
+ def initialize(*args)
20
+ @cookies = {}
21
+ @deleted_cookies = []
22
+ if args.any?
23
+ cookies = args.first
24
+ cookies.keys.each do |cookie_name|
25
+ self[cookie_name] = { value: cookies[cookie_name] }
26
+ end
27
+ end
28
+ end
29
+
30
+ def deleted?(name)
31
+ @deleted_cookies.include?(name)
32
+ end
33
+
34
+ def to_hash
35
+ @cookies
36
+ end
37
+
38
+ def metadata_for(name)
39
+ @cookies[name]
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cookie_cutter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andy Alm
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Provides a way to define the structure, lifetime, and other properties
63
+ of a cookie all in one place.
64
+ email:
65
+ - private
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - .travis.yml
72
+ - Gemfile
73
+ - LICENSE
74
+ - README.md
75
+ - Rakefile
76
+ - cookie_cutter.gemspec
77
+ - lib/cookie_cutter.rb
78
+ - lib/cookie_cutter/base.rb
79
+ - lib/cookie_cutter/cookie.rb
80
+ - lib/cookie_cutter/version.rb
81
+ - spec/base_spec.rb
82
+ - spec/support/fake_cookie_jar.rb
83
+ homepage: http://github.com/andyalm/cookie_cutter
84
+ licenses: []
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ segments:
96
+ - 0
97
+ hash: -1951188585045254267
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ segments:
105
+ - 0
106
+ hash: -1951188585045254267
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 1.8.24
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: Provides a way to define the structure, lifetime, and other properties of
113
+ a cookie all in one place.
114
+ test_files:
115
+ - spec/base_spec.rb
116
+ - spec/support/fake_cookie_jar.rb