hashing 0.0.1.beta.2 → 0.0.1

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.
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