hashema 0.0.2 → 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/README.md CHANGED
@@ -2,9 +2,141 @@
2
2
 
3
3
  Hashema lets you validate JSONable objects (hashes and arrays) against a schema, and assert their validity in your RSpec examples.
4
4
 
5
- ## Validating an object against a schema
5
+ ## Installation
6
6
 
7
- The API of `Hashema::Validator` consists of an initializer and two instance methods: `valid?` and `failure_message`. The initializer takes an object to validate and a schema, in that order. `valid?` will return `true` iff the object conforms to the schema. If `valid?` is `false`, `failure_message` will return a description of the failure.
7
+ ```bash
8
+ gem install hashema
9
+ ```
10
+
11
+ Or, if you're using [bundler](https://rubygems.org/gems/bundler), put this in your `Gemfile`:
12
+
13
+ ```bash
14
+ gem 'hashema'
15
+ ```
16
+
17
+ Hashema hooks into your RSpec config to provide the `conform_to_schema` matcher. If `rspec` is listed in your `Gemfile`, you should be able to use `conform_to_schema` in your tests with no further setup.
18
+
19
+ ## RSpec usage
20
+
21
+ With hashema and RSpec, it's easy to ensure your JSON APIs return the data your clients expect.
22
+
23
+ ```ruby
24
+ describe BlogSerializer do
25
+ before { @serializer = BlogSerializer.new(Blog.create) }
26
+
27
+ describe '#as_json' do
28
+ subject { @serializer.as_json }
29
+
30
+ SCHEMA = {
31
+ url: /^https?:\/\/.+/,
32
+ posts: [
33
+ { title: String,
34
+ published: [true, false]
35
+ }
36
+ ]
37
+ }
38
+
39
+ it { is_expected_to conform_to_schema SCHEMA }
40
+ end
41
+ end
42
+ ```
43
+
44
+ ## The Schema DSL by example
45
+
46
+ ### Allowing any value
47
+
48
+ ```ruby
49
+ expect(
50
+ Rotation.new('squirrel')
51
+ ).to conform_to_schema Object
52
+ ```
53
+
54
+ ### Checking for an exact match
55
+
56
+ ```ruby
57
+ expect(
58
+ {error: 'not found'}
59
+ ).to conform_to_schema({error: 'not found'})
60
+ ```
61
+
62
+ ### Checking for membership in a class
63
+
64
+ ```ruby
65
+ expect(
66
+ {berzerker: 'pasta'}
67
+ ).to conform_to_schema Hash
68
+ ```
69
+
70
+ ### Checking that a string value matches a regular expression
71
+
72
+ ```ruby
73
+ expect(
74
+ 'Hello! My name is Fridge.'
75
+ ).to conform_to_schema /^Hello! My name is \w+\.$/
76
+ ```
77
+
78
+ ### Checking for inclusion in a set of alternatives
79
+
80
+ ```ruby
81
+ expect(
82
+ {is_awesome: true}
83
+ ).to conform_to_schema({is_awesome: [true, false]})
84
+ ```
85
+
86
+ ### Checking for inclusion in a range of legal values
87
+
88
+ ```ruby
89
+ expect(
90
+ {kyu_rank: 17}
91
+ ).to conform_to_schema({kyu_rank: 1..30})
92
+ ```
93
+
94
+ ### Checking that all elements of an array share a schema
95
+
96
+ ```ruby
97
+ expect(
98
+ [{name: 'Melody'}, {name: 'Elias'}, {name: 'Yoda'}]
99
+ ).to conform_to_schema [{name: String}]
100
+ ```
101
+
102
+ ### Matching an array of items that may have different schemas
103
+
104
+ ```ruby
105
+ expect(
106
+ [{cash: '12.33'}, {credit: '28.95'}, {cash: '40.70'}]
107
+ ).to conform_to_schema(
108
+ [
109
+ [{cash: /^\d+\.\d\d$/}, {credit: /^\d+\.\d\d$/}]
110
+ ]
111
+ )
112
+ ```
113
+
114
+ ## RSpec matcher options
115
+
116
+ ### with_indifferent_access
117
+
118
+ Rails fans will be familiar with ActiveSupport's `HashWithIndifferentAccess`, which treats symbol and string keys as interchangeable. Calling `with_indifferent_access` on the `conform_to_schema` matcher will make the matcher similarly tolerant, allowing you to match a hash with string keys against a schema with symbol keys. This is especially useful when writing schemas for JSON, since parsed objects will always have string keys.
119
+
120
+ ```ruby
121
+ get :show
122
+
123
+ schema = {
124
+ url: /^https?:\/\/.+/,
125
+ posts: [
126
+ { title: String,
127
+ published: [true, false]
128
+ }
129
+ ]
130
+ }
131
+
132
+ expect(JSON(response.body)).to conform_to_schema(schema).with_indifferent_access
133
+ ```
134
+
135
+ ## Hashema without RSpec
136
+
137
+ There are times when you want to validate the structure of a data object in your production code. For example, if your program parses data from a user-created file, you might want to check that the data you read in match the schema you expect. For such situations, you can use `Hashema::Validator`.
138
+
139
+ The API of `Hashema::Validator` consists of an initializer and two instance methods: `valid?` and `failure_message`. The initializer takes an object to validate and a schema, in that order. `valid?` will return `true` iff the object conforms to the schema.
8
140
 
9
141
  ```ruby
10
142
  validator = Hashema::Validator.new(
@@ -37,8 +169,45 @@ validator = Hashema::Validator.new(
37
169
  validator.valid? # true
38
170
  ```
39
171
 
40
- ## RSpec matcher usage
172
+ If `valid?` is `false`, `failure_message` will return a human-readable description of the failure, which includes, at a minimum:
173
+
174
+ - the path through the data structure to the point where the first mismatch occurred
175
+ - the expected value at that point
176
+ - the actual value
41
177
 
42
178
  ```ruby
43
- expect(datum).to conform_to_schema schema
179
+ validator = Hashema::Validator.new(
180
+ # the object to validate
181
+ { blog:
182
+ { url: 'http://www.blagoblag.com',
183
+ posts: [
184
+ { title: 'hello',
185
+ published: true
186
+ },
187
+ { title: 123,
188
+ published: false
189
+ }
190
+ ]
191
+ }
192
+ },
193
+
194
+ # the schema
195
+ { blog:
196
+ { url: /^https?:\/\//,
197
+ posts: [
198
+ { title: String,
199
+ published: [true, false]
200
+ }
201
+ ]
202
+ }
203
+ }
204
+ )
205
+
206
+ validator.valid? # false
207
+ puts validator.failure_message
208
+ # prints:
209
+ # expected /blog/posts/1/title to match
210
+ # String
211
+ # but got
212
+ # 123
44
213
  ```
@@ -1,7 +1,5 @@
1
1
  require 'hashema/validator'
2
-
3
- begin
4
- require 'rspec'
5
- require 'hashema/conform_to_schema'
6
- rescue LoadError => e
7
- end
2
+ require 'hashema/version'
3
+ require 'hashema/conform_to_schema'
4
+ require 'hashema/compiler'
5
+ require 'hashema/schema'
@@ -0,0 +1,38 @@
1
+ require_relative './schema'
2
+
3
+ module Hashema
4
+ class Compiler
5
+ def self.compile(thing, options={})
6
+ new(options).compile(thing)
7
+ end
8
+
9
+ def initialize(options={})
10
+ @options = options
11
+ end
12
+
13
+ def compile(thing)
14
+ case thing
15
+ when ::Array
16
+ if thing.size == 1
17
+ Hashema::Array.new(compile(thing[0]))
18
+ else
19
+ Hashema::Alternatives.new(*(thing.map { |element| compile element }))
20
+ end
21
+ when ::Hash
22
+ compile_hash(thing)
23
+ else
24
+ Hashema::Atom.new(thing)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def compile_hash(hash)
31
+ with_compiled_values = ::Hash[hash.map { |k, v| [k, compile(v)]}]
32
+ klass = @options[:indifferent_access] ?
33
+ Hashema::HashWithIndifferentAccess :
34
+ Hashema::Hash
35
+ klass.new(with_compiled_values)
36
+ end
37
+ end
38
+ end
@@ -1,24 +1,71 @@
1
- RSpec::Matchers.define :conform_to_schema do |schema|
2
- match do |actual|
3
- @schema = schema
4
- @actual = actual
5
- @validator = Hashema::Validator.new(actual, schema)
6
- @validator.valid?
7
- end
1
+ begin
2
+ require 'rspec'
3
+ rescue LoadError => e
4
+ end
8
5
 
9
- def failure_message
10
- @validator.failure_message
11
- end
6
+ module Hashema
7
+ module RSpecMatchers
8
+ class ConformToSchema
9
+ def initialize(schema)
10
+ @schema = schema
11
+ @with_indifferent_access = false
12
+ end
13
+
14
+ def with_indifferent_access
15
+ @with_indifferent_access = true
16
+ self
17
+ end
18
+
19
+ def matches?(actual)
20
+ @validator = Hashema::Validator.new(actual, @schema, validator_options)
21
+ @validator.valid?
22
+ end
23
+
24
+ def failure_message
25
+ @validator.failure_message
26
+ end
27
+
28
+ def failure_message_for_should
29
+ failure_message
30
+ end
12
31
 
13
- def failure_message_for_should
14
- failure_message
32
+ def failure_message_when_negated
33
+ # TODO: @actual is nil here. This probably doesn't work.
34
+ "expected\n#{@actual.inspect}\nnot to match schema\n#{@schema.inspect}"
35
+ end
36
+
37
+ def failure_message_for_should_not
38
+ failure_message_when_negated
39
+ end
40
+
41
+ def description
42
+ "match schema\n#{@schema.inspect}"
43
+ end
44
+
45
+ def validator_options
46
+ {indifferent_access: @with_indifferent_access}
47
+ end
48
+ end
49
+
50
+ def conform_to_schema(schema)
51
+ ConformToSchema.new(schema)
52
+ end
15
53
  end
54
+ end
55
+
56
+ if Kernel.const_defined? 'RSpec'
57
+ class Hashema::RSpecMatchers::ConformToSchema
58
+ include RSpec::Matchers::Composable
16
59
 
17
- def failure_message_when_negated
18
- "expected\n#{@actual.inspect}\nnot to match schema\n#{@schema.inspect}"
60
+ RSpec::Matchers.alias_matcher(
61
+ :an_object_conforming_to_schema,
62
+ :conform_to_schema
63
+ ) do |description|
64
+ description.sub("match schema\n", 'an object conforming to schema ')
65
+ end
19
66
  end
20
67
 
21
- def failure_message_for_should_not
22
- failure_message_when_negated
68
+ RSpec.configure do |config|
69
+ config.include Hashema::RSpecMatchers
23
70
  end
24
71
  end
@@ -0,0 +1,259 @@
1
+ module Hashema
2
+ class Schema < Struct.new(:expected)
3
+ # A Schema is a Comparison factory.
4
+ def compare(actual)
5
+ self.class.const_get('Comparison').new(actual, expected)
6
+ end
7
+
8
+ def inspect
9
+ expected.inspect
10
+ end
11
+ end
12
+
13
+ class Comparison < Struct.new(:actual, :expected)
14
+ def match?
15
+ mismatches.empty?
16
+ end
17
+
18
+ def mismatches
19
+ @mismatches ||= find_mismatches
20
+ end
21
+
22
+ private
23
+
24
+ def find_mismatches
25
+ raise NotImplementedError.new(
26
+ "#{self.class.name} must implement find_mismatches"
27
+ )
28
+ end
29
+ end
30
+
31
+ class Atom < Schema
32
+ class Comparison < Hashema::Comparison
33
+
34
+ private
35
+
36
+ def find_mismatches
37
+ expected === actual ? [] : [Mismatch.new(actual, expected, [])]
38
+ end
39
+ end
40
+ end
41
+
42
+ class Array < Schema
43
+ class Comparison < Hashema::Comparison
44
+
45
+ private
46
+
47
+ def find_mismatches
48
+ type_mismatches || element_mismatches
49
+ end
50
+
51
+ def type_mismatches
52
+ expectation = "be an Array, but got #{actual.class}"
53
+ actual.is_a?(::Array) ? nil : [Mismatch.new(actual, ::Array, [], expectation)]
54
+ end
55
+
56
+ def element_mismatches
57
+ actual.each_with_index.flat_map do |element, i|
58
+ element_comparison = expected.compare(element)
59
+
60
+ element_comparison.mismatches.map do |mismatch|
61
+ Mismatch.at i, mismatch
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ class Map < Schema
69
+ class Comparison < Hashema::Comparison
70
+
71
+ private
72
+
73
+ def find_mismatches
74
+ type_mismatch || (keyset_mismatches + value_mismatches)
75
+ end
76
+
77
+ def type_mismatch
78
+ expectation = "be a #{expected_class}, but got #{actual.class}"
79
+ actual.is_a?(expected_class) ?
80
+ nil :
81
+ [Mismatch.new(actual, expected_class, [], expectation)]
82
+ end
83
+
84
+ def expected_class
85
+ raise NotImplementedError.new "#{self.class.name} must implement expected_class"
86
+ end
87
+
88
+ def value_mismatches
89
+ matching_keys.flat_map do |key|
90
+ comparison = fetch(key, expected).compare(fetch(key, actual))
91
+
92
+ comparison.mismatches.map do |mismatch|
93
+ Mismatch.at key, mismatch
94
+ end
95
+ end
96
+ end
97
+
98
+ def keyset_mismatches
99
+ if extra_keys.empty? && missing_keys.empty?
100
+ []
101
+ else
102
+ missing_keys_expectation = missing_keys.any? ?
103
+ "\nmissing keys were:\n\t#{missing_keys.map(&:inspect).join("\n\t")}" :
104
+ ''
105
+
106
+ extra_keys_expectation = extra_keys.any? ?
107
+ "\nextra keys were:\n\t#{extra_keys.map(&:inspect).join("\n\t")}" :
108
+ ''
109
+
110
+ expectation = "have a different set of keys" +
111
+ missing_keys_expectation +
112
+ extra_keys_expectation
113
+
114
+ [Mismatch.new(actual, expected, [], expectation)]
115
+ end
116
+ end
117
+
118
+ def extra_keys
119
+ raise NotImplementedError.new "#{self.class.name} must implement extra_keys"
120
+ end
121
+
122
+ def missing_keys
123
+ raise NotImplementedError.new "#{self.class.name} must implement missing_keys"
124
+ end
125
+
126
+ def matching_keys
127
+ raise NotImplementedError.new "#{self.class.name} must implement matching_keys"
128
+ end
129
+
130
+ def fetch(key, from_map)
131
+ raise NotImplementedError.new "#{self.class.name} must implement fetch"
132
+ end
133
+ end
134
+ end
135
+
136
+ class Hash < Schema
137
+ class Comparison < Hashema::Map::Comparison
138
+
139
+ private
140
+
141
+ def expected_class
142
+ ::Hash
143
+ end
144
+
145
+ def extra_keys
146
+ @extra_keys ||= actual_keys - expected_keys
147
+ end
148
+
149
+ def missing_keys
150
+ @missing_keys ||= expected_keys - actual_keys
151
+ end
152
+
153
+ def matching_keys
154
+ @matching_keys ||= expected.keys & actual.keys
155
+ end
156
+
157
+ def fetch(key, hash)
158
+ hash[key]
159
+ end
160
+
161
+ def expected_keys
162
+ @expected_keys ||= Set.new(expected.keys)
163
+ end
164
+
165
+ def actual_keys
166
+ @actual_keys ||= Set.new(actual.keys)
167
+ end
168
+ end
169
+ end
170
+
171
+ class Alternatives < Schema
172
+ def initialize(*args)
173
+ super(args)
174
+ end
175
+
176
+ class Comparison < Hashema::Comparison
177
+
178
+ private
179
+
180
+ def find_mismatches
181
+ if expected.none? { |alternative| alternative.compare(actual).match? }
182
+ [Mismatch.new(actual, expected, [])]
183
+ else
184
+ []
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ class HashWithIndifferentAccess < Schema
191
+ class Comparison < Hashema::Map::Comparison
192
+
193
+ private
194
+
195
+ def expected_class
196
+ ::Hash
197
+ end
198
+
199
+ def extra_keys
200
+ @extra_keys ||= actual.keys.reject do |key|
201
+ expected.has_key? symbol_to_string key or
202
+ expected.has_key? string_to_symbol key
203
+ end
204
+ end
205
+
206
+ def missing_keys
207
+ @missing_keys ||= expected.keys.reject do |key|
208
+ actual.has_key? symbol_to_string key or
209
+ actual.has_key? string_to_symbol key
210
+ end
211
+ end
212
+
213
+ def matching_keys
214
+ @matching_keys ||=
215
+ Set.new(expected.keys.map(&method(:symbol_to_string))) &
216
+ Set.new(actual.keys.map(&method(:symbol_to_string)))
217
+ end
218
+
219
+ def fetch(key, hash)
220
+ return hash[symbol_to_string key] if hash.has_key? symbol_to_string key
221
+ return hash[string_to_symbol key] if hash.has_key? string_to_symbol key
222
+ end
223
+
224
+ def string_to_symbol(key)
225
+ key.is_a?(String) ? key.to_sym : key
226
+ end
227
+
228
+ def symbol_to_string(key)
229
+ key.is_a?(Symbol) ? key.to_s : key
230
+ end
231
+
232
+ def expected_keys
233
+ @expected_keys ||= Set.new(expected.keys.map(&method(:symbol_to_string)))
234
+ end
235
+
236
+ def actual_keys
237
+ @actual_keys ||= Set.new(actual.keys.map(&method(:symbol_to_string)))
238
+ end
239
+ end
240
+ end
241
+
242
+ class Mismatch < Struct.new(:actual, :expected, :location, :verb)
243
+ def self.at(location, original)
244
+ new original.actual,
245
+ original.expected,
246
+ [location] + original.location
247
+ end
248
+
249
+ def message
250
+ "expected /#{location.join '/'} to #{verb}"
251
+ end
252
+
253
+ private
254
+
255
+ def verb
256
+ super || "match\n\t#{expected.inspect}\nbut got\n\t#{actual.inspect}"
257
+ end
258
+ end
259
+ end
@@ -1,120 +1,29 @@
1
+ require_relative './compiler'
2
+
1
3
  module Hashema
2
4
  class Validator
3
- def initialize(actual, schema)
5
+ def initialize(actual, schema, options={})
4
6
  @actual = actual
5
7
  @schema = schema
6
- @withholding_judgement = false
7
- match! @actual, @schema
8
+ @schema = compile schema, options
8
9
  end
9
10
 
10
11
  def valid?
11
- !!@match
12
+ comparison.match?
12
13
  end
13
14
 
14
15
  def failure_message
15
- @mismatch
16
+ comparison.mismatches[0].message
16
17
  end
17
18
 
18
19
  private
19
20
 
20
- def match!(actual, schema, path=[])
21
- @match = begin
22
- if schema.is_a? Hash
23
- match_hash! actual, schema, path
24
- elsif schema.is_a? Array and schema.length == 1
25
- match_array! actual, schema, path
26
- elsif schema.is_a? Array and schema.length > 1
27
- match_alternatives! actual, schema, path
28
- else
29
- match_with_triple_equals! actual, schema, path
30
- end
31
- end
32
- end
33
-
34
- def match_hash!(actual, schema, path)
35
- if actual.is_a? Hash
36
- if schema.keys.sort == actual.keys.sort
37
- match_hash_with_same_keys! actual, schema, path
38
- else
39
- report_mismatched_key_sets! actual, schema, path
40
- end
41
- else
42
- report_error "expected #{format_path path} to be a Hash, but got #{actual.class}"
43
- false
44
- end
45
- end
46
-
47
- def match_hash_with_same_keys!(actual, schema, path)
48
- recording_mismatches actual, schema, path do
49
- actual.all? do |key, value|
50
- match! value, schema[key], path + [key]
51
- end
52
- end
53
- end
54
-
55
- def report_mismatched_key_sets!(actual, schema, path)
56
- extras = actual.keys - schema.keys
57
- missing = schema.keys - actual.keys
58
- error = "expected #{format_path path} to have a different set of keys\n"
59
- error += "the extra keys were:\n #{extras.map(&:inspect).join("\n ")}\n" if extras.any?
60
- error += "the missing keys were:\n #{missing.map(&:inspect).join("\n ")}\n" if missing.any?
61
- report_error error
62
- false
63
- end
64
-
65
- def match_array!(actual, schema, path)
66
- if actual.is_a? Array
67
- recording_mismatches actual, schema, path do
68
- actual.is_a? Array and
69
- actual.each_with_index.all? { |elem, i| match! elem, schema[0], path + [i] }
70
- end
71
- else
72
- report_error "expected #{format_path path} to be an Array, but got #{actual.class}"
73
- false
74
- end
75
- end
76
-
77
- def match_alternatives!(actual, alternatives, path)
78
- recording_mismatches actual, alternatives, path do
79
- alternatives.any? do |alternative|
80
- withholding_judgement actual, alternatives, path do
81
- match! actual, alternative, path
82
- end
83
- end
84
- end
85
- end
86
-
87
- def match_with_triple_equals!(actual, expected, path)
88
- recording_mismatches actual, expected, path do
89
- expected === actual
90
- end
91
- end
92
-
93
- def withholding_judgement(actual, schema, path)
94
- original_withholding_judgement = @withholding_judgement
95
- @withholding_judgement = true
96
- returned = yield
97
- @withholding_judgement = original_withholding_judgement
98
- returned
99
- end
100
-
101
- def recording_mismatches(actual, schema, path)
102
- if yield
103
- true
104
- else
105
- report_error "expected #{format_path path} to match\n#{schema.inspect}\nbut got\n#{actual.inspect}"
106
- false
107
- end
108
- end
109
-
110
- def report_error(error)
111
- unless @withholding_judgement
112
- @mismatch ||= error
113
- end
21
+ def compile(schema, options={})
22
+ Hashema::Compiler.compile(schema, options)
114
23
  end
115
24
 
116
- def format_path(path)
117
- "/#{path.join("/")}"
25
+ def comparison
26
+ @comparison ||= @schema.compare(@actual)
118
27
  end
119
28
  end
120
29
  end
@@ -1,5 +1,5 @@
1
1
  module Hashema
2
2
  module Version
3
- STRING = '0.0.2'
3
+ STRING = '0.1.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,27 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Ben Christel
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-08-22 00:00:00.000000000 Z
12
+ date: 2015-05-04 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: rspec
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: 2.0.0
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: 2.0.0
27
30
  description: Assert that JSONable objects conform to a schema
@@ -32,7 +35,9 @@ extra_rdoc_files:
32
35
  - README.md
33
36
  files:
34
37
  - lib/hashema.rb
38
+ - lib/hashema/compiler.rb
35
39
  - lib/hashema/conform_to_schema.rb
40
+ - lib/hashema/schema.rb
36
41
  - lib/hashema/validator.rb
37
42
  - lib/hashema/version.rb
38
43
  - License.txt
@@ -40,27 +45,27 @@ files:
40
45
  homepage: http://github.com/benchristel/hashema
41
46
  licenses:
42
47
  - MIT
43
- metadata: {}
44
48
  post_install_message:
45
49
  rdoc_options:
46
50
  - --charset=UTF-8
47
51
  require_paths:
48
52
  - lib
49
53
  required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
50
55
  requirements:
51
- - - '>='
56
+ - - ! '>='
52
57
  - !ruby/object:Gem::Version
53
58
  version: '0'
54
59
  required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
55
61
  requirements:
56
- - - '>='
62
+ - - ! '>='
57
63
  - !ruby/object:Gem::Version
58
64
  version: '0'
59
65
  requirements: []
60
66
  rubyforge_project:
61
- rubygems_version: 2.0.0
67
+ rubygems_version: 1.8.23
62
68
  signing_key:
63
- specification_version: 4
64
- summary: hashema-0.0.2
69
+ specification_version: 3
70
+ summary: hashema-0.1.0
65
71
  test_files: []
66
- has_rdoc:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 0320697b5755730fd8ffa18843b0f3b496cfd739
4
- data.tar.gz: 8bf8bcedb3d51e880d5a13c499754c3fc604a5c0
5
- SHA512:
6
- metadata.gz: f8e5882479f1cbb3a9923575812eafc7d78281a69fd070f6ac488468e4b9d002ccfaca37b41717522df1d8429a2559251b07c321941f09a388f99a28aaafda12
7
- data.tar.gz: 98a5238131421fb6fe61ff625cf2cbd71ffaaada4d2280df5079f25540709aa0e6cdb872336eb05a229d6819116ff2d894ffe3640cb5db1a03b7c35c5d154a7b