hashing 0.0.1.beta.2 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bd1eb92fa4ef69893588e90462ba6671051ce607
4
- data.tar.gz: 22ce96b6cb54a66f481a0f8ecfeda9337e908353
3
+ metadata.gz: 58fbbbe8dddf5ac8299796af0123035abba17b68
4
+ data.tar.gz: 142748d477848bc270bdd7fc4c74be5bcd6335ab
5
5
  SHA512:
6
- metadata.gz: 008576591fcb187f2bb97ac1524e6c52f13359e814cd8a0370899581c6c2186e45516293ee9de51544fd17caa1fd6854d73bc27535243f87881d86b1c67f6fb1
7
- data.tar.gz: 1d37ef8ac7bfd7ffc832add6c6fa0ab754ee5a8079eadaa27bf239008867a73a3eed83637b1a2c63152e30f4dd198f7f42576ca2f3d24f17d9c261007dde9828
6
+ metadata.gz: a5488323cd2dc9d7d53b2f2c53eeb091b7283b7f3d37be183d2311e67fd2cc5484ce7c02a070051d40ae7e30db87c718ee97f276dd332f9034321ae9f3600dbb
7
+ data.tar.gz: bbfa91cdfcacc250f95b9a9a1ff66986cb281db6007b8f95dac0198641f446c3fd10a7ad8681a07c455e4f3299952aadea8332cf2b06eb30e4878715dec8e7ab
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ script: 'bundle exec rake'
3
+ rvm:
4
+ - 2.1.1
5
+ - 2.0.0
6
+ - 1.9.3
7
+ - jruby-19mode
data/Gemfile CHANGED
@@ -1,4 +1,2 @@
1
1
  source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in hasher.gemspec
4
2
  gemspec
data/README.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # Hashing
2
2
 
3
- Permits you to say which instance variables of your objects should be used to
4
- serialize it into a `Hash`. Gives you a nice way to inform this, and facilitates
5
- to recreation of your serializable objects from hashes.
3
+ Gives you an easy way to specify which instances vars of your objects should be
4
+ used as `key => value` to serialize it into a hash returned by the `#to_h`
5
+ method. Also gives you a `YourClass::from_hash` to reconstruct the instances.
6
+
7
+ ## Status
8
+ [![Gem Version](https://badge.fury.io/rb/hashing.svg)](http://badge.fury.io/rb/hashing)
9
+ [![Build Status](https://travis-ci.org/ricardovaleriano/hashing.png?branch=master)](http://travis-ci.org/ricardovaleriano/hashing?branch=master)
10
+ [![Code Climate](https://codeclimate.com/github/ricardovaleriano/hashing.png)](https://codeclimate.com/github/ricardovaleriano/hashing)
11
+ [![Inline docs](http://inch-pages.github.io/github/ricardovaleriano/hashing.png)](http://inch-pages.github.io/github/ricardovaleriano/hashing)
6
12
 
7
13
  ## Installation
8
14
 
@@ -78,7 +84,7 @@ class File
78
84
  include Hashing
79
85
  hasherize :path, :commit, :content
80
86
 
81
- from_hash ->(hash) {
87
+ loading ->(hash) {
82
88
  new hash[:path], hash[:commit], hash[:content]
83
89
  }
84
90
 
@@ -105,7 +111,7 @@ The previous example can be writen like this:
105
111
  class File
106
112
  include Hasherize.new :path, :commit, :content
107
113
 
108
- from_hash ->(hash) {
114
+ loading ->(hash) {
109
115
  new hash[:path], hash[:commit], hash[:content]
110
116
  }
111
117
 
@@ -138,7 +144,7 @@ class File
138
144
  to_hash: ->(content) { Base64.encode64 content },
139
145
  from_hash: ->(content_string) { Base64.decode64 content_string }
140
146
 
141
- from_hash ->(hash) {
147
+ loading ->(hash) {
142
148
  new hash[:path], hash[:commit], hash[:content]
143
149
  }
144
150
 
@@ -146,6 +152,20 @@ class File
146
152
  end
147
153
  ```
148
154
 
155
+ #### Custom hasherizing and loading strategies for multiple ivars
156
+
157
+ You can indicate the same strategies for hasherzing and load from hashes for
158
+ multiple `ivars` if it makes sense to your program:
159
+
160
+ ```ruby
161
+ class File
162
+ include Hashing
163
+
164
+ hasherize :path, :commit,
165
+ to_hash: ->(value) { value.downcase },
166
+ from_hash: ->(value) { value.downcase }
167
+ end
168
+ ```
149
169
 
150
170
  #### Nested hasherized objects
151
171
 
@@ -203,7 +223,14 @@ this is all that `Hashing` needs to build a `File` instances from a valid `Hash`
203
223
 
204
224
  ## Contributing
205
225
 
206
- 1. Fork it ( https://github.com/[my-github-username]/hasher/fork )
226
+ This was a rapid "scratch your own itch" kind of project. It will make me real
227
+ happy if it can be used used in your software anyhow. If you need something
228
+ different than what is in it, or can solve us some bugs or add documentation, it
229
+ will be very well received!
230
+
231
+ Here is how you can help this gem:
232
+
233
+ 1. Fork it ( https://github.com/ricardovaleriano/hashing/fork )
207
234
  2. Create your feature branch (`git checkout -b my-new-feature`)
208
235
  3. Commit your changes (`git commit -am 'Add some feature'`)
209
236
  4. Push to the branch (`git push origin my-new-feature`)
data/Rakefile CHANGED
@@ -1,2 +1,13 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+ Dir[File.join(File.dirname(__FILE__), 'tasks/*.rake')].each { |rake| load rake }
4
+ task default: "test"
5
+
6
+ desc 'line number statistics'
7
+ task :lines do
8
+ libdir = File.join File.dirname(__FILE__), 'lib'
9
+ Dir["lib/**/*.rb"].each { |file|
10
+ lines = File.readlines(file).reject { |line| line =~ /^(\s*)#/ }.count
11
+ puts "#{lines.to_s.rjust(3)} in #{file}"
12
+ }
13
+ end
data/hashing.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Ricardo Valeriano"]
10
10
  spec.email = ["ricardo.valeriano@gmail.com"]
11
11
  spec.summary = %q{Serialize your objects as Hashes}
12
- #spec.description = %q{TODO: Write a longer description. Optional.}
12
+ spec.description = %q{Gives you an easy way to specify which instances vars of your objects should be used as `key => value` to serialize it into a hash returned by the `#to_h` method. Also gives you a `YourClass::from_hash` to reconstruct the instances.}
13
13
  spec.homepage = "http://github.com/ricardovaleriano/hashing"
14
14
  spec.license = "MIT"
15
15
 
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
22
  spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest-reporters", "~> 1.0"
23
24
  end
data/lib/hasherize.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'hashing'
2
+
3
+ # Provides some sugar syntax to declare which `ivars` should be used to
4
+ # represent an object as a `Hash`.
5
+ #
6
+ # It respects all the behavior you will get by including {Hashing}. In fact,
7
+ # using this constructor is a shortcut to `include Hashing`, and call
8
+ # `.hasherize`
9
+ #
10
+ # @since 0.0.1
11
+ #
12
+ # @example shortcut to `include Hashing` and call `.hasherize`
13
+ # class File
14
+ # include Hasherize.new :path, :commit
15
+ # end
16
+ #
17
+ # @example configuring :to_hash and :from_hash strategies
18
+ # class File
19
+ # include Hasherize.new :content,
20
+ # to_hash: ->(content) { Base64.encode64 content },
21
+ # from_hash: ->(content_string) { Base64.decode64 content_string }
22
+ # end
23
+ class Hasherize < Module
24
+ # Stores the ivars and options to be repassed to `Hashing.serialize` by the
25
+ # hook {#included}
26
+ #
27
+ # @param ivars_and_options [*args] ivar names and options (`:to_hash` and `:from_hash`)
28
+ def initialize(*ivars_and_options)
29
+ @ivars = ivars_and_options
30
+ end
31
+
32
+ # Includes the `Hashing` module and calls {Hashing.hasherize}, repassing the
33
+ # ivar names an the options received in the constructor
34
+ def included(serializable_class)
35
+ serializable_class.module_eval do
36
+ include Hashing
37
+ end
38
+ serializable_class.send :hasherize, *@ivars
39
+ end
40
+ end
@@ -0,0 +1,94 @@
1
+ module Hashing
2
+ # Represents each one of the instance variables in a class that should be used
3
+ # to represent an object in a `Hash` form (serialization).
4
+ class Ivar
5
+ attr_reader :name
6
+
7
+ # Configure the name of an `ivar` and the 'callable' objects thath will be
8
+ # used to prepare the `ivar` value for serialization, and to load the object
9
+ # from a `Hash`.
10
+ #
11
+ # @param name [Symbol] name of a class `ivar`
12
+ # @param to_h [#call] callable to transform the value when serializing
13
+ # @param from_hash [#call] callable to transform the value from a `Hash`
14
+ def initialize(name, to_h = nil, from_hash = nil)
15
+ @name = name.to_sym
16
+ @to_h = to_h
17
+ @from_hash = from_hash
18
+ end
19
+
20
+ # Processes the parameter acordingly to the configuration made in the
21
+ # constructor. If some was made, return the value after being processed or
22
+ # else return the value as it is.
23
+ #
24
+ # Also guarantee that if a value is a #map, every item with `Hashing` in his
25
+ # method lookup will be sent the message `#to_h`.
26
+ #
27
+ # @param value [Object] object to be processed before being stored in a `Hash`
28
+ # @return the value that will be stored in the `Hash`
29
+ def to_h(value)
30
+ return value unless @to_h
31
+
32
+ if value.respond_to? :map
33
+ value = hasherize value
34
+ end
35
+ @to_h.call value
36
+ end
37
+
38
+ # Processes the Object provinient from a `Hash` so it can be used to
39
+ # reconstruct an instance.
40
+ #
41
+ # @param value [Object] object provinient from a hash
42
+ # @return the value that will be used to reconstruct the original instance
43
+ def from_hash(value, metadata = {})
44
+ return value unless @from_hash
45
+
46
+ if value.respond_to? :map
47
+ value = normalize(value, metadata)
48
+ end
49
+ @from_hash.call value
50
+ end
51
+
52
+ def to_sym
53
+ @name.to_sym
54
+ end
55
+
56
+ def to_s
57
+ @name.to_s
58
+ end
59
+
60
+ private
61
+ # Is an object descendent of {Hashing}?
62
+ #
63
+ # @param value [Object]
64
+ def hashing?(value)
65
+ value.class.ancestors.include? Hashing
66
+ end
67
+
68
+ # Hasherize a value when it has {Hashing} in it's method lookup or return
69
+ # the value. Util when a collection of {Hashing} objects is given and need
70
+ # to be "hasherized"
71
+ #
72
+ # @param value [#map] the value to be verified as a {Hashing} (or not)
73
+ # @return [#map] collection of hashes
74
+ def hasherize(collection)
75
+ collection.map { |item| hashing?(item) ? item.to_h : item }
76
+ end
77
+
78
+ # If a collection of {Hashing} objects is given, we have to reconstruct all
79
+ # collections members before while reconstructing the collection itself.
80
+ # This method provides that
81
+ #
82
+ # TODO: (need?) recursion to reconstruct collections of collections
83
+ #
84
+ # @param value [#map] the collection of {Hashing} objects
85
+ # @param metadata [Hash] containing serialized data about the original object
86
+ # @return [#map] collection of {Hashing} instances
87
+ def normalize(collection, metadata)
88
+ elements_class = metadata.fetch(:types, {}).fetch(@name, nil)
89
+ return collection unless elements_class.respond_to? :from_hash
90
+
91
+ collection.map { |element| elements_class.from_hash element }
92
+ end
93
+ end
94
+ end
@@ -1,3 +1,3 @@
1
1
  module Hashing
2
- VERSION = "0.0.1.beta.2"
2
+ VERSION = "0.0.1"
3
3
  end
data/lib/hashing.rb CHANGED
@@ -1,5 +1,170 @@
1
- require "hashing/version"
1
+ require 'hashing/version'
2
+ require 'hashing/ivar'
3
+ require 'hasherize'
2
4
 
3
5
  module Hashing
4
- # Your code goes here...
6
+ # Inform the user about an attempt to create an instance, using a `Hash` with
7
+ # keys that does not correspond to the mape made using `.hasherize`
8
+ class UnconfiguredIvar < StandardError
9
+ def initialize(ivar_name, class_name)
10
+ super "The Hash has a :#{ivar_name} key, "+
11
+ "but no @#{ivar_name} was configured in #{class_name}"
12
+ end
13
+ end
14
+
15
+ # Inject the public api into the client class.
16
+ #
17
+ # @since 0.0.1
18
+ #
19
+ # @example including Hashing
20
+ # require 'hashing'
21
+ #
22
+ # class File
23
+ # include Hashing
24
+ # hasherize :path, :commit
25
+ #
26
+ # def initialize(path, commit)
27
+ # @path, @commit = path, commit
28
+ # end
29
+ # end
30
+ #
31
+ # When `Hashing` is included, the host class will gain the `.from_hash({})`
32
+ # method and the `#to_h` instance method.
33
+ # Another method that will be added is the private class method `.hasherize`
34
+ # will be added so you can indicate what ivars do you want in your sarialized
35
+ # objects.
36
+ def self.included(client_class)
37
+ client_class.extend Hasherizer
38
+ end
39
+
40
+ def meta_data(name, value)
41
+ @_hashing_meta_data ||= { __hashing__: { types: {} } }
42
+ @_hashing_meta_data[:__hashing__][:types][name] = value
43
+ @_hashing_meta_data
44
+ end
45
+
46
+ #
47
+ # The `Hash` returned by `#to_h` will be formed by keys based on the ivars
48
+ # names passed to `hasherize` method.
49
+ #
50
+ # @example File hahserized (which include `Hashing`)
51
+ #
52
+ # file = File.new 'README.md', 'cfe9aacbc02528b'
53
+ # file.to_h
54
+ # # => { path: 'README.md', commit: 'cfe9aacbc02528b' }
55
+ def to_h
56
+ hash_pairs = self.class.ivars.map { |ivar|
57
+ value = instance_variable_get "@#{ivar}"
58
+ if value.respond_to? :map
59
+ meta_data ivar.to_sym, value.first.class
60
+ end
61
+ [ivar.to_sym, ivar.to_h(value)]
62
+ }
63
+ Hash[hash_pairs].merge(@_hashing_meta_data || {})
64
+ end
65
+
66
+ # Define the class methods that should be available in a 'hasherized ®' class
67
+ # (a class that include `Hashing`).
68
+ module Hasherizer
69
+ # Configures which instance variables will be used to compose the `Hash`
70
+ # generated by `#to_h`
71
+ #
72
+ # @api
73
+ # @param ivars_and_options [*arguments]
74
+ def hasherize(*ivars_and_options)
75
+ @ivars ||= []
76
+ ivars = extract_ivars ivars_and_options
77
+ to_strategy = strategy_for_key :to_hash, ivars_and_options
78
+ from_strategy = strategy_for_key :from_hash, ivars_and_options
79
+ @ivars += ivars.map { |ivar| Ivar.new ivar, to_strategy, from_strategy }
80
+ end
81
+
82
+ # Configures the strategy to (re)create an instance of the 'hasherized ®'
83
+ # class based on a `Hash` instance. This strategy will be used by the
84
+ # `.from_hash({})` method.
85
+ #
86
+ # This configuration is optional, if it's not called, then the strategy will
87
+ # be just repassing the `Hash` to the initializer.
88
+ #
89
+ # @param strategy [#call]
90
+ # @return void
91
+ def loading(strategy)
92
+ @strategy = strategy
93
+ end
94
+
95
+ # Provides the default strategy for recreate objects from hashes (which is
96
+ # just call .new passing the `Hash` as is.
97
+ #
98
+ # @return the result of calling the strategy
99
+ def strategy
100
+ @strategy || ->(h) { new h }
101
+ end
102
+
103
+ # those methods are private but part of the class api (macros).
104
+ # #TODO: there is a way to document the 'macros' for a class in YARD?
105
+ private :hasherize, :loading, :strategy
106
+
107
+ # provides access to the current configuration on what `ivars` should be
108
+ # used to generate a `Hash` representation of instances of the client class.
109
+ #
110
+ # @return [Array] ivars that should be included in the final Hash
111
+ def ivars
112
+ @ivars ||= []
113
+ end
114
+
115
+ # Receives a `Hash` and uses the strategy configured by `.loading` to
116
+ # (re)create an instance of the 'hasherized ®' class.
117
+ #
118
+ # @param pairs [Hash] in a valid form defined by `.hasherize`
119
+ # @return new object
120
+ def from_hash(pairs)
121
+ metadata = pairs.delete(:__hashing__) || {}
122
+ hash_to_load = pairs.map do |ivar_name, value|
123
+ ivar = ivar_by_name ivar_name.to_sym
124
+ [ivar.to_sym, ivar.from_hash(value, metadata)]
125
+ end
126
+ strategy.call Hash[hash_to_load]
127
+ end
128
+
129
+ private
130
+ # Cleanup the arguments received by `.hasherize` so only the `ivar` names
131
+ # are returned. This is necessarey since the `.hasherize` can receive a
132
+ # `Hash` with strategies `:from_hash` and `:to_hash` as the last argument.
133
+ #
134
+ # @param ivars_and_options [Array] arguments received by `.serialize`
135
+ # @return [Array[:Symbol]] ivar names that should be used in the `Hash` serialization
136
+ def extract_ivars(ivars_and_options)
137
+ ivars = ivars_and_options.dup
138
+ if ivars.last.is_a? Hash
139
+ ivars.pop
140
+ end
141
+ ivars
142
+ end
143
+
144
+ # Fetches the strategy to serialize or deserialize (defined by the first
145
+ # param `strategy`) the `ivars` passed as second parameter
146
+ #
147
+ # @param hash_key [Symbol] (:to_hash || :from_hash) type of strategy to fetch
148
+ # @param ivars_and_options [*args | *args, Hash]
149
+ # @return [#call] strategy to be used
150
+ def strategy_for_key(strategy, ivars_and_options)
151
+ default_strategy_just_returns = ->(ivar_value) { ivar_value }
152
+ strategies = { strategy => default_strategy_just_returns }
153
+ strategies = ivars_and_options.last if ivars_and_options.last.is_a? Hash
154
+ strategies[strategy]
155
+ end
156
+
157
+ # Search an `ivar` by it's name in the class ivars collection
158
+ #
159
+ # #TODO: Can be enhanced since now the ivars doesn't have a sense of
160
+ # equality.
161
+ #
162
+ # @param ivar_name [Symbol] `ivar` name
163
+ # @return [Ivar]
164
+ def ivar_by_name(ivar_name)
165
+ ivar = ivars.select { |ivar| ivar.to_sym == ivar_name }.first
166
+ raise UnconfiguredIvar.new ivar_name, name unless ivar
167
+ ivar
168
+ end
169
+ end
5
170
  end
data/tasks/test.rake ADDED
@@ -0,0 +1,26 @@
1
+ desc "run all tests"
2
+
3
+ args = ARGV
4
+ args.shift
5
+ files = args
6
+
7
+ desc "run all the tests"
8
+
9
+ task :test do
10
+ lib = File.expand_path("../lib", __FILE__)
11
+ $: << lib
12
+
13
+ loader = ->(files_list){
14
+ files_list.each do |f|
15
+ if File.directory?(f)
16
+ loader.call(Dir.glob(f + "/**/*_test.rb"))
17
+ else
18
+ load f
19
+ end
20
+ end
21
+ }
22
+
23
+ require_relative "../test/test_helper"
24
+ files = Dir.glob("test/**/*_test.rb") unless files.size > 0
25
+ loader.call(files)
26
+ end
@@ -0,0 +1,32 @@
1
+ describe Hasherize do
2
+ let(:hasherized) do
3
+ Class.new do
4
+ attr_reader :file, :commit
5
+
6
+ include Hasherize.new :file, :commit,
7
+ to_hash: ->(value) { "X-#{value}" },
8
+ from_hash: ->(value) { "#{value}-X" }
9
+
10
+ loading ->(params) { new params[:file], params[:commit] }
11
+
12
+ def initialize(file, commit)
13
+ @file, @commit = file, commit
14
+ end
15
+ end
16
+ end
17
+
18
+ it "just sugar to `include Hashing` and call `hasherize`" do
19
+ hasherized.ancestors.include?(Hashing).must_be :==, true
20
+ end
21
+
22
+ it "configures the ivars correctly (so I can recreate instances by Hash)" do
23
+ object = hasherized.from_hash file: 'README.md', commit: 'omglolbbq123'
24
+ object.file.must_be :==, 'README.md-X'
25
+ object.commit.must_be :==, 'omglolbbq123-X'
26
+ end
27
+
28
+ it "configure `:to_hash` e `:from_hash` serialization strategies" do
29
+ object = hasherized.new 'README.md', 'omglolbbq123'
30
+ object.to_h.must_be :==, { file: 'X-README.md', commit: 'X-omglolbbq123' }
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ describe Hashing::Ivar do
2
+ it "knows it's ivar name" do
3
+ ivar = Hashing::Ivar.new :file
4
+ ivar.name.must_be :==, :file
5
+ end
6
+
7
+ describe '#to_h' do
8
+ it "uses the lambda passed as parameter to transform the value" do
9
+ called = false
10
+ ivar = Hashing::Ivar.new :file, ->(value) { called = value }
11
+ ivar.to_h 'some value'
12
+ called.must_be :==, 'some value'
13
+ end
14
+
15
+ it "by default just returns the value as is" do
16
+ Hashing::Ivar.new(:file).to_h('x').must_be :==, 'x'
17
+ end
18
+ end
19
+
20
+ describe '#from_hash' do
21
+ it "uses the lambda passed as parameter to transform the value" do
22
+ called = false
23
+ ivar = Hashing::Ivar.new :file, nil, ->(value) { called = value }
24
+ ivar.from_hash 'loading hash'
25
+ called.must_be :==, 'loading hash'
26
+ end
27
+
28
+ it "by default just returns the value as is" do
29
+ Hashing::Ivar.new(:file).from_hash('x').must_be :==, 'x'
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,76 @@
1
+ describe Hashing do
2
+ describe 'when one of the ivars is an `Array` of hasherized objects' do
3
+ class HashingCollectionOwner
4
+ attr_reader :file, :commit, :annotations
5
+ include Hasherize.new :file, :commit, :annotations
6
+
7
+ loading ->(hash) { new hash[:file], hash[:commit], hash[:annotations] }
8
+
9
+ def initialize(file, commit, annotations)
10
+ @file, @commit, @annotations = file, commit, annotations
11
+ end
12
+ end
13
+
14
+ class HashingCollectionMember
15
+ attr_reader :annotation
16
+ include Hasherize.new :annotation,
17
+ to_hash: ->(value) { "--#{value}" },
18
+ from_hash: ->(value) { "#{value}--" }
19
+
20
+ loading ->(hash) { new hash[:annotation] }
21
+
22
+ def initialize(annotation)
23
+ @annotation = annotation
24
+ end
25
+ end
26
+
27
+ describe '#to_h' do
28
+ it 'calls #to_h for each array item when hashifying the object' do
29
+ owner = HashingCollectionOwner.new 'README.md', 'cfe9aacbc02528b', [
30
+ HashingCollectionMember.new('first'),
31
+ HashingCollectionMember.new('second'),
32
+ ]
33
+ owner.to_h.must_be :==, {
34
+ file: 'README.md',
35
+ commit: 'cfe9aacbc02528b',
36
+ annotations: [
37
+ { annotation: '--first' },
38
+ { annotation: '--second' },
39
+ ],
40
+ __hashing__: {
41
+ types: {
42
+ annotations: HashingCollectionMember
43
+ }
44
+ }
45
+ }
46
+ p owner.to_h
47
+ end
48
+
49
+ it "don't call the #to_h on inner object that don't include `Hashing`"
50
+ end
51
+
52
+ describe '#from_h' do
53
+ let(:hash_values) do
54
+ {
55
+ file: "README.md",
56
+ commit: "cfe9aacbc02528b",
57
+ annotations: [
58
+ {annotation: "first"},
59
+ {annotation: "second"}
60
+ ],
61
+ __hashing__: {
62
+ types: {
63
+ annotations: HashingCollectionMember
64
+ }
65
+ }
66
+ }
67
+ end
68
+
69
+ it 'calls #from_h for each on an yaml array that contains hasherized objects' do
70
+ owner = HashingCollectionOwner.from_hash hash_values
71
+ owner.annotations.first.annotation.must_be :==, 'first--'
72
+ owner.annotations.last.annotation.must_be :==, 'second--'
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,123 @@
1
+ require "base64"
2
+
3
+ describe Hashing do
4
+ describe 'interface' do
5
+ let(:hasherized) do
6
+ # if in doubt about the absense of assertions in this test, please
7
+ # refer to:
8
+ # - http://blog.zenspider.com/blog/2012/01/assert_nothing_tested.html
9
+ # and https://github.com/seattlerb/minitest/issues/159
10
+ Class.new do
11
+ include Hashing
12
+ hasherize :ivar
13
+ loading ->() {}
14
+ end
15
+ end
16
+
17
+ it 'adds the ::from_hash method' do
18
+ hasherized.respond_to?(:from_hash).must_be :==, true
19
+ end
20
+
21
+ it 'adds the #to_h method' do
22
+ hasherized.new.respond_to?(:to_h).must_be :==, true
23
+ end
24
+ end# interface
25
+
26
+ describe 'Recreating a `hasherized` class instance' do
27
+ let(:hasherized) do
28
+ Class.new do
29
+ attr_reader :h
30
+
31
+ include Hashing
32
+ hasherize :h
33
+
34
+ def initialize(h)
35
+ @h = h
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '.loading' do
41
+ it 'uses (`#call`) the strategy defined by `.loading`' do
42
+ called = false
43
+ my_strategy = ->(h) { called = true }
44
+ hasherized.send :loading, my_strategy
45
+ hasherized.from_hash Hash.new
46
+ called.must_be :==, true
47
+ end
48
+ end
49
+
50
+ describe '#from_hash' do
51
+ it 'just calls .new if none strategy was defined by .loading' do
52
+ new_object = hasherized.from_hash h: 'hasherizing'
53
+ new_object.h.must_be :==, { h: 'hasherizing' }
54
+ end
55
+
56
+ it 'give an informative message in case the Hash is malformed' do
57
+ OmgLolBBQ = hasherized
58
+ message = nil
59
+ proc {
60
+ begin
61
+ OmgLolBBQ.from_hash xpto: 'JUST NO!'
62
+ rescue => e
63
+ message = e.message
64
+ raise e
65
+ end
66
+ }.must_raise Hashing::UnconfiguredIvar
67
+ message.must_be :==, 'The Hash has a :xpto key, but no @xpto '+
68
+ 'was configured in OmgLolBBQ'
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '.hasherize' do
74
+ let(:hasherized) do
75
+ Class.new do
76
+ include Hashing
77
+
78
+ attr_reader :content
79
+
80
+ hasherize :file, :commit
81
+ hasherize :content,
82
+ to_hash: ->(content) { Base64.encode64(content) },
83
+ from_hash: ->(hash_string) { Base64.decode64(hash_string) }
84
+ loading ->(hash) { new hash[:file], hash[:commit], hash[:content] }
85
+
86
+ def initialize(file, commit, content)
87
+ @file, @commit, @content = file, commit, content
88
+ end
89
+ end
90
+ end
91
+
92
+ it 'allows configure how to serialize a specific `ivar`' do
93
+ file = hasherized.new 'README.md', 'cfe9aacbc02528b', '#Hashing\n\nWow. Such code...'
94
+ file.to_h[:content].must_be :==, Base64.encode64('#Hashing\n\nWow. Such code...')
95
+ end
96
+
97
+ it 'allows configure how to load a value for a specific `ivar`' do
98
+ file = hasherized.from_hash file: 'README.md',
99
+ commit: 'cfe9aacbc02528b',
100
+ content: Base64.encode64('#Hashing\n\nWow. Such code...')
101
+ file.content.must_be :==, '#Hashing\n\nWow. Such code...'
102
+ end
103
+ end
104
+
105
+ describe '#to_h' do
106
+ let(:hasherized) do
107
+ Class.new do
108
+ include Hashing
109
+ hasherize :file, :commit
110
+ loading ->(hash) { new hash[:file], hash[:commit] }
111
+
112
+ def initialize(file, commit)
113
+ @file, @commit = file, commit
114
+ end
115
+ end
116
+ end
117
+
118
+ it 'creates a hash using the ivars based on `.hasherize`' do
119
+ object = hasherized.new 'README.md', 'cfe9aacbc02528b'
120
+ object.to_h.must_be :==, { file: 'README.md', commit: 'cfe9aacbc02528b' }
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,5 @@
1
+ require "minitest/autorun"
2
+ require "minitest/reporters"
3
+ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
4
+
5
+ require_relative "../lib/hashing"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.beta.2
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ricardo Valeriano
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-26 00:00:00.000000000 Z
11
+ date: 2014-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: '1.5'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '1.6'
26
+ version: '1.5'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,7 +38,23 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- description:
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest-reporters
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: Gives you an easy way to specify which instances vars of your objects
56
+ should be used as `key => value` to serialize it into a hash returned by the `#to_h`
57
+ method. Also gives you a `YourClass::from_hash` to reconstruct the instances.
42
58
  email:
43
59
  - ricardo.valeriano@gmail.com
44
60
  executables: []
@@ -46,13 +62,22 @@ extensions: []
46
62
  extra_rdoc_files: []
47
63
  files:
48
64
  - .gitignore
65
+ - .travis.yml
49
66
  - Gemfile
50
67
  - LICENSE.txt
51
68
  - README.md
52
69
  - Rakefile
53
70
  - hashing.gemspec
71
+ - lib/hasherize.rb
54
72
  - lib/hashing.rb
73
+ - lib/hashing/ivar.rb
55
74
  - lib/hashing/version.rb
75
+ - tasks/test.rake
76
+ - test/hasherize_test.rb
77
+ - test/hashing/ivar_test.rb
78
+ - test/hashing/nested_test.rb
79
+ - test/hashing_test.rb
80
+ - test/test_helper.rb
56
81
  homepage: http://github.com/ricardovaleriano/hashing
57
82
  licenses:
58
83
  - MIT
@@ -68,13 +93,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
93
  version: '0'
69
94
  required_rubygems_version: !ruby/object:Gem::Requirement
70
95
  requirements:
71
- - - '>'
96
+ - - '>='
72
97
  - !ruby/object:Gem::Version
73
- version: 1.3.1
98
+ version: '0'
74
99
  requirements: []
75
100
  rubyforge_project:
76
101
  rubygems_version: 2.1.11
77
102
  signing_key:
78
103
  specification_version: 4
79
104
  summary: Serialize your objects as Hashes
80
- test_files: []
105
+ test_files:
106
+ - test/hasherize_test.rb
107
+ - test/hashing/ivar_test.rb
108
+ - test/hashing/nested_test.rb
109
+ - test/hashing_test.rb
110
+ - test/test_helper.rb