polaroid 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bcc0c39513e0b9468561e2d3bcf13e917dda9fb1
4
+ data.tar.gz: 5f61fe411b563dc1bddef7389edda3684ad6eb78
5
+ SHA512:
6
+ metadata.gz: 2eed4a6e80f15591b16bec0e972a53439957a44f1595c4dc5930116efcaa44d549c50895c37f8293fb8cc90c53e36d43196f02bc50956e536bf1e882f34496ed
7
+ data.tar.gz: 32aca9025f1217ad821a220534096465ccbcc697345e512dfa5aec3fda4ced875fe650dcb1c17eb6e0426f30acfd00aade004e402c046e2250599a6f9c0b3718
@@ -0,0 +1,17 @@
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
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in polaroid.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Paul Kwiatkowski
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.
@@ -0,0 +1,110 @@
1
+ # Polaroid
2
+
3
+ Polaroid provides shortcuts to capture the state of a Ruby object, and can construct a fake object later to mimic that state.
4
+
5
+ ## Why?
6
+
7
+ Never send a Hash to do an Object's job. Method dispatch is faster and method calls look cleaner than nested\['braces'\]\['everywhere'\] in your code.
8
+
9
+ ## But why would you ever need that?
10
+
11
+ The use case that inspired Polaroid involved using a background worker to send an Email. The email was being templated from a single View-Model object. Rather than shuffling database primary keys across a queue (be it Redis, or a real messaging system like Rabbit) and bothering the database later, we can just build the View-Model now, capture its state as JSON, and send that off to the Mailer.
12
+
13
+ But that requires the templates now access data from a Hash. And you should never send a Hash to do an Object's job.
14
+
15
+ Well, since we were already pulling these values from an object, why not just push them back into a Fake that shares the same interface of the View-Model? Or at least as much interface as we care about.
16
+
17
+ This is just one use case I have found.
18
+
19
+ ## Simple Example
20
+
21
+ In a class of which you would like to have a Snapshot, include a new instance of Polaroid, passing it the list of messages you would like to capture. These are not limited to attributes, but can be any message name. It is highly encouraged that the chosen messages be idempotent and side-effect-free wherever possible.
22
+
23
+ An example of a Person class with two attributes, and a method which selects
24
+
25
+ ```ruby
26
+ class Person
27
+ include Polaroid.new(:name, :age, :favorite_drinks)
28
+
29
+ attr_reader :name, :age, :favorites
30
+
31
+ def initialize(name, age, favorites)
32
+ @name = name
33
+ @age = age
34
+ @favorites = favorites
35
+ end
36
+
37
+ def favorite_drinks
38
+ favorites.select { |fav| drink?(fav) }
39
+ end
40
+
41
+ def favorite_foods
42
+ favorites.select { |fav| food?(fav) }
43
+ end
44
+
45
+ def drink?(str)
46
+ %w[coffee beer wine tea water juice].include?(str)
47
+ end
48
+
49
+ def food?(str)
50
+ %w[omelete burrito ramen pie yogurt].include?(str)
51
+ end
52
+ end
53
+ ```
54
+
55
+ And if we make a `Person` instance, we can take a snapshot, which by default is just a `Hash`.
56
+
57
+ ```ruby
58
+ pat = Person.new('Patrick', 25, ['beer', 'coffee', 'ramen', 'pie'])
59
+ # => #<Person @name="Patrick", @age=25, @favorites=["beer", "coffee", "ramen", "pie"]>
60
+ pat.age
61
+ # => 25
62
+ pat.favorite_drinks
63
+ # => ["beer", "coffee"]
64
+ pat.favorite_foods
65
+ # => ["ramen", "pie"]
66
+ snapshot = pat.take_shapshot
67
+ # => { name: "Patrick", age: 25, favorite_drinks: ["beer", "coffee"] }
68
+ ```
69
+
70
+ But now we can take that snapshot Hash and build a fake Person (actual class is `Person::Snapshot`) that responds to all the messages we chose to record from the original `Person`:
71
+
72
+ ```ruby
73
+ fake_pat = Person.build_from_snapshot(snapshot)
74
+ # => #<struct Person::Snapshot name="Patrick", age=25, favorite_drinks=["beer", "coffee"]>
75
+ fake_pat.age
76
+ # => 25
77
+ fake_pat.favorite_drinks
78
+ # => ["beer", "coffee"]
79
+ ```
80
+
81
+ However, since we did not record the whole `favorites` array, nor did we ask the message `favorite_foods` be included our snapshot, the `Person::Snapshot` does not respond to `favorite_foods`:
82
+
83
+ ```ruby
84
+ fake_pat.favorite_foods
85
+ # => NoMethodError: undefined method `favorite_foods' for #<Person::Snapshot>
86
+ ```
87
+
88
+ ## Hashes? But I want JSON!
89
+
90
+ Of course you do. So the `take_snapshot` and `build_from_snapshot` methods also take a trailing `format` parameter:
91
+
92
+ ```ruby
93
+ pat = Person.new('Patrick', 25, ['beer', 'coffee', 'ramen', 'pie'])
94
+ json_snapshot = snapshot.take_snapshot(pat, :json)
95
+ # => "{\"name\":\"Patrick\",\"age\":25,\"favorite_drinks\":[\"beer\",\"coffee\"]}"
96
+ Person.build_from_snapshot(json_snapshot, :json)
97
+ # => #<struct Person::Snapshot name="Patrick", age=25, favorite_drinks=["beer", "coffee"]>
98
+ ```
99
+
100
+ Right now the only accepted formats are `:hash` and `:json`. If there are other serialization formats that people care about, I would be happy to have them included.
101
+
102
+
103
+ ## Contributing
104
+
105
+ 1. Fork it ( http://github.com/swifthand/polaroid/fork )
106
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
107
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
108
+ 4. Push to the branch (`git push origin my-new-feature`)
109
+ 5. Create new Pull Request
110
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,47 @@
1
+ class Polaroid < Module
2
+
3
+ VERSION = "0.0.3"
4
+
5
+ def initialize(*messages)
6
+ @messages = messages
7
+ @polaroid_struct_class = ImmutableStruct.new(*messages)
8
+ define_capture_method
9
+ freeze
10
+ end
11
+
12
+ # Build the fake class for internal use in the including class' namespace.
13
+ def included(base)
14
+ base.const_set(:Snapshot, @polaroid_struct_class)
15
+ base.extend(ClassMethods)
16
+ end
17
+
18
+
19
+ private #######################################################################
20
+
21
+ # Give the class a #take_snapshot instance method, which records the result
22
+ # of sending an instance the assigned messages, and returns them as a Hash.
23
+ def define_capture_method
24
+ messages = @messages
25
+ take_snapshot = ->(format = :hash) {
26
+ snapshot = self.class::Snapshot.new(*(messages.map { |msg| self.send(msg) })).to_h
27
+ format == :json ? snapshot.to_json : snapshot
28
+ }
29
+ define_method(:take_snapshot, &take_snapshot)
30
+ end
31
+
32
+
33
+ module ClassMethods
34
+ def build_from_snapshot(snapshot_hash, format = :hash)
35
+ case format
36
+ when :hash
37
+ # This line intentionally left blank
38
+ when :json
39
+ snapshot_hash = JSON.parse(snapshot_hash).map.with_object({}) do |(k, v), hash|
40
+ hash[k.to_sym] = v
41
+ end
42
+ end
43
+ self::Snapshot.new(snapshot_hash)
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'polaroid'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "polaroid"
8
+ spec.version = Polaroid::VERSION
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.authors = ["Paul Kwiatkowski"]
11
+ spec.email = ["paul@groupraise.com"]
12
+ spec.summary = %q{Polaroid provides shortcuts to capture the state of a Ruby object, and can construct a fake object later to mimic that state.}
13
+ spec.description = %q{Polaroid provides shortcuts to capture the state of a Ruby object, and can construct a fake object later to represents that state. The goal is to "Never send a Hash to do an Object's job" when performing common tasks such as, sending data to a background worker as JSON, or otherwise.}
14
+ spec.homepage = "https://github.com/swifthand/polaroid"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ # spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.5"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+
25
+ spec.add_runtime_dependency "immutable_struct", "~> 1.1"
26
+ spec.add_runtime_dependency "json", "~> 1.8"
27
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: polaroid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Paul Kwiatkowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: immutable_struct
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.8'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.8'
69
+ description: Polaroid provides shortcuts to capture the state of a Ruby object, and
70
+ can construct a fake object later to represents that state. The goal is to "Never
71
+ send a Hash to do an Object's job" when performing common tasks such as, sending
72
+ data to a background worker as JSON, or otherwise.
73
+ email:
74
+ - paul@groupraise.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - lib/polaroid.rb
85
+ - polaroid.gemspec
86
+ homepage: https://github.com/swifthand/polaroid
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.2.1
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Polaroid provides shortcuts to capture the state of a Ruby object, and can
110
+ construct a fake object later to mimic that state.
111
+ test_files: []