fixed_record 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0eea6e2937878d931df67a139cff38882002163f62f43e9f6b4e9440c84bbc60
4
- data.tar.gz: 8d51b0834f97783271b77fd8762289efad419c463aa3a6f705affded0a07de2e
3
+ metadata.gz: 156863124ec2fb0ca1668bafa2b16de94d622f577ea554ace2189dd24758bfa4
4
+ data.tar.gz: caf2f4fb5e63bd96218dc25eeeda8ede9bf792acc35bebb992aecb2068a395de
5
5
  SHA512:
6
- metadata.gz: 39687e91690bd8426119d2c41b77298dc3a48c2da4000a57bc6160f14ea2eb021d4495eac2ca27d50189fa5fcedbb2196c4c543bda44ff936aba2cf27d4f9b42
7
- data.tar.gz: '0697f532ce75a0e808fefe5a94a9fa42b9fa00e346c7e6dda1288066aab857ab36d196a57473134d04d66ad4aa0b4b91a4db57a8197284a5c49ff9cfaf18b432'
6
+ metadata.gz: 0c40bf0b31215154be4a30ee76c4db815a88b24d3f1fa6e8f3be0ddd27225db9e22c0a446a6c624adb313bf6e83ef45e12f2f4b2c2a9e7801811fa2966ef3108
7
+ data.tar.gz: a567503b4d12425d0ca89931707c14a41ec8b026986a51ff18676042bec51d15eaac45171faf8d13edc3c49489f7ffea62294becc5d7221e31b78db5374a72e8
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Gem fixed_record Changelog
2
+
3
+ ## 0.3.0
4
+ * Add optional required and optional arguments to specify required and optional fields respectively
5
+
6
+ ## 0.2.0
7
+ * Add support for a Hash of Hashes in the YAML file, and for
8
+ accessing records by key.
9
+ * Added CHANGELOG.md
10
+
11
+ ## 0.1.x
12
+ * Initial release
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fixed_record (0.2.0)
4
+ fixed_record (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -48,7 +48,7 @@ Then to load these, create a class
48
48
  require 'fixed_record'
49
49
 
50
50
  class MyFavoriteWebsite < FixedRecord
51
- data "#{Rails.root}/data/my_favorite_websites.yml"
51
+ data "#{Rails.root}/data/my_favorite_websites.yml", required: [:name, :url]
52
52
 
53
53
  # Return hostname of url for company
54
54
  def hostname
@@ -88,7 +88,8 @@ StaticPage#first:
88
88
  description: Welcome to the First Page
89
89
 
90
90
  StaticPage#last:
91
- title: Last Page
91
+ title: Last Page
92
+ short_title: LastP
92
93
  description: Welcome to the Last Page
93
94
 
94
95
  ```
@@ -99,7 +100,7 @@ Then to load these, create a class
99
100
  require 'fixed_record'
100
101
 
101
102
  class MyWebPages < FixedRecord
102
- data "#{Rails.root}/data/my_web_pages.yml"
103
+ data "#{Rails.root}/data/my_web_pages.yml", required: [:title,:description], optional: [:short_title]
103
104
 
104
105
  end
105
106
  ```
@@ -140,12 +141,35 @@ The declared class will also include all the methods from the `Enumerable` modul
140
141
  Some basic sanity checks are performed on the YAML file to catch common errors:
141
142
 
142
143
  * It must define a non-empty array or hash of records
143
- * All records must have the same set of attributes
144
+ * If the optional `required:` or `optional:` arguments are given, then each record _must_ have all the required fields and _may_ also have any of the optional fields.
145
+ * If niether the `required:` or `optional:` arguments are given, then each record _must_ have the same set of fields.
146
+
147
+ An `ArgumentError` exception will be thrown if any validation errors are detected. A system-dependent error (probably `Errno::ENOENT`) will be thrown if the file cannot be read.
148
+
149
+ Additional validations can be performed by defining a `validate` method. e.g.
150
+
151
+ ```ruby
152
+ require 'fixed_record'
153
+ require 'uri'
154
+
155
+ class MyFavoriteWebsite < FixedRecord
156
+ data "#{Rails.root}/data/my_favorite_websites.yml", required: [:name, :url]
157
+
158
+ # Check that the url can be parsed
159
+ def validate( values, index )
160
+ begin
161
+ URI.parse(url)
162
+ rescue URI::InvalidURIError -> e
163
+ raise e.class, "#{filename} index #{index} has invalid url: #{e.message}"
164
+ end
165
+ end
166
+
167
+ end
168
+ ```
169
+
170
+
144
171
 
145
- An `ArgumentError` exception will be thrown if any errors are detected.
146
172
 
147
- Additional validations can be performed by overriding the `validate_yaml` and
148
- `validate_item` class functions.
149
173
 
150
174
 
151
175
  ## Development
data/lib/fixed_record.rb CHANGED
@@ -1,21 +1,33 @@
1
1
 
2
2
  require 'yaml'
3
+ require 'set'
3
4
 
4
5
  class FixedRecord
5
- VERSION = "0.2.0"
6
+ VERSION = "0.3.0"
6
7
 
7
8
  # Lazy load data from given filename
8
9
  # creating accessors for top level attributes
9
- def self.data( filename )
10
+ def self.data( filename, required: [], optional: [] )
11
+ required = required.map( &:to_s )
12
+ optional = optional.map( &:to_s )
13
+ throw ArgumentError, "Required and Optional names overlap" unless (required & optional).empty?
14
+
15
+ valid_keys = Set.new( required )
16
+ valid_keys.merge( optional )
17
+ required_keys = Set.new( required )
18
+
19
+ self.class_variable_set( :@@filename, filename )
20
+ self.class_variable_set( :@@required_keys, required_keys )
21
+ self.class_variable_set( :@@valid_keys, valid_keys )
22
+ self.class_variable_set( :@@items, nil )
23
+
10
24
  class_eval %Q{
11
25
  class << self
12
26
  include Enumerable
13
27
  end
14
28
 
15
- @@items = nil
16
-
17
29
  def self.filename
18
- %Q{#{filename}}
30
+ @@filename
19
31
  end
20
32
 
21
33
  def self.all
@@ -50,47 +62,59 @@ class FixedRecord
50
62
  def self.load!
51
63
  if @@items.nil?
52
64
  y = YAML.load_file( filename )
53
- validate_yaml( y )
54
- valid_keys = nil
65
+ validate_structure( y )
55
66
  if y.is_a?(Array)
56
- valid_keys = y.first.keys
67
+ if @@valid_keys.empty?
68
+ @@valid_keys = y.first.keys
69
+ @@required_keys = @@valid_keys
70
+ end
57
71
  @@items = y.map.with_index do |values,i|
58
- validate_item( valid_keys, values, i )
72
+ validate_item( @@valid_keys, @@required_keys, values, i )
59
73
  r = new
60
74
  r.instance_variable_set( :@values, values )
61
75
  r
62
76
  end
63
77
  elsif y.is_a?(Hash)
64
78
  @@items = Hash.new
65
- add_key = true
79
+ add_key = !@@valid_keys.member?('key')
66
80
  y.each do |k,values|
67
- if valid_keys.nil?
68
- valid_keys = values.keys
69
- add_key = !values.has_key?('key')
81
+ if @@valid_keys.empty?
82
+ @@required_keys.merge( values.keys )
83
+ @@valid_keys.merge( values.keys )
84
+ add_key = !@@valid_keys.member?('key')
70
85
  end
71
- validate_item( valid_keys, values, k )
86
+ validate_item( @@valid_keys, @@required_keys, values, k )
72
87
  values['key'] = k if add_key
73
88
  r = new
74
89
  r.instance_variable_set( :@values, values )
75
90
  @@items[k] = r
76
91
  end
77
- valid_keys << 'key' if add_key
92
+ define_method( :key ) { @values['key'] } if add_key
78
93
  end
79
- create_methods( valid_keys )
94
+ create_methods( @@valid_keys )
80
95
  end
81
96
  end
82
97
  }
83
98
  end
84
99
 
100
+
101
+ # Override this to perform additional entries. It gets passed the hash containing the
102
+ # values for each record. index is either a record index (0 based) or a key associated
103
+ # with the record
104
+ def self.validate( values, index )
105
+ end
106
+
107
+ private
108
+
85
109
  # Create access methods for each of valid_keys
86
110
  def self.create_methods( valid_keys )
87
- valid_keys.each do |k|
88
- define_method( k.to_sym) { @values[k] }
111
+ valid_keys.each do |key|
112
+ define_method( key.to_sym) { @values[key] }
89
113
  end
90
114
  end
91
115
 
92
116
  # Validate the top level of the data structure returned
93
- def self.validate_yaml( y )
117
+ def self.validate_structure( y )
94
118
  if y.is_a?(Array)
95
119
  if y.length <= 0
96
120
  throw ArgumentError.new "#{filename} contain a zero length array"
@@ -112,14 +136,16 @@ class FixedRecord
112
136
  end
113
137
 
114
138
  # Validate a values of name -> value
115
- def self.validate_item( valid_keys, values, index )
139
+ def self.validate_item( valid_keys, required_keys, values, index )
116
140
  raise ArgumentError, "#{filename} item #{index} should be name value pairs" unless values.is_a?(Hash)
117
- raise ArgumentError, "#{filename} item #{index} has wrong number of values" if valid_keys.length != values.length
118
- valid_keys.each do |name|
119
- unless values.has_key? name
120
- raise ArgumentError, "#{filename} item #{index} is missing value for '#{name}'"
121
- end
141
+ required_keys.each do |name|
142
+ raise ArgumentError, "#{filename} item #{index} is missing value for '#{name}'" unless values.has_key?(name)
143
+ end
144
+ values.keys.each do |v|
145
+ raise ArgumentError, "#{filename} item #{index} has unexpected value for '#{name}'" unless valid_keys.include?(v)
122
146
  end
147
+ # User can implement this to add extra validation
148
+ validate( values, index )
123
149
  end
124
150
 
125
151
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fixed_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Bell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-22 00:00:00.000000000 Z
11
+ date: 2020-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -62,6 +62,7 @@ files:
62
62
  - ".gitignore"
63
63
  - ".rspec"
64
64
  - ".travis.yml"
65
+ - CHANGELOG.md
65
66
  - Gemfile
66
67
  - Gemfile.lock
67
68
  - README.md