ruson 1.2.0 → 1.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: df8812fa2f70ad440c16a4647961a68955b3909d481db38bd6271534afce154c
4
- data.tar.gz: d8877e9e6f435d319a736e0157845dae2264ba6468a2ed918b158601de2a8881
3
+ metadata.gz: d3b5eacde447ed93d7ecffd81ba095d20dad1398687f5138dd7f2267bfd5bacf
4
+ data.tar.gz: 172098d6792639c7c785eba55332b323785c879ce59458766d60787458b043bc
5
5
  SHA512:
6
- metadata.gz: 5883b0fc7b4e95a713a906f83c1d3f07a2761cd1cf4a246f2ae6ee2591322fd24791c8ce99f37dc6e62338c4da55494d160428206838de34154e5402efcad409
7
- data.tar.gz: 20fb24a41b36de01dcb8bfcb32a864ed88808f49229617a610ceb5ced0ee8a0ca3bf4204c5100120349fee63f76a79948cd09f7d4b16afcb02e34dc8a0afd44e
6
+ metadata.gz: 1e33b1b04e784cc26494d08fb02a26c9afa9f42bd3595c6f4527da6f59ce937c8df8b4d4b8d95bf528d6ecdbc9384cddc80de738b59d5224098fd9c4c45da3da
7
+ data.tar.gz: 5cb1a2daa61f905266afb804878a59a0f617e19d530649651c2fbd978c9854c87c243d1002c7baa4e11bc95b6590e75207f4a2af5942941444d378693cbec1f2
@@ -0,0 +1,30 @@
1
+ # How to use it
2
+ # =============
3
+ #
4
+ # Visit https://blog.zedroot.org/2015/04/30/using-docker-to-maintain-a-ruby-gem/
5
+
6
+ # ~~~~ Image base ~~~~
7
+ # Base image
8
+ FROM ruby:2.6.5-alpine
9
+ LABEL maintainer="klriutsa"
10
+
11
+ RUN mkdir -p /gem/
12
+ WORKDIR /gem/
13
+ ADD . /gem/
14
+
15
+ # ~~~~ OS Maintenance & Rails Preparation ~~~~
16
+ # Rubygems and Bundler
17
+ RUN apk add --no-cache \
18
+ --quiet \
19
+ build-base \
20
+ git \
21
+ && touch ~/.gemrc \
22
+ && echo "gem: --no-ri --no-rdoc" >> ~/.gemrc \
23
+ && gem install rubygems-update \
24
+ && update_rubygems \
25
+ && gem install bundler \
26
+ && bundle install \
27
+ && rm -rf /var/cache/apk/*
28
+
29
+ ENTRYPOINT ["bundle", "exec"]
30
+ CMD ["rake", "-T"]
data/README.md CHANGED
@@ -20,6 +20,8 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
+ ### Basic
24
+
23
25
  post.json
24
26
  ```json
25
27
  {
@@ -42,7 +44,7 @@ post.title #=> 'Ruson'
42
44
  post.content #=> 'Welcome!'
43
45
  ```
44
46
 
45
- ### name
47
+ #### name
46
48
 
47
49
  post.json
48
50
  ```json
@@ -65,7 +67,7 @@ post = Post.new(json)
65
67
  post.url #=> 'http://sample.com'
66
68
  ```
67
69
 
68
- ### nilable
70
+ #### nilable
69
71
 
70
72
  post.json
71
73
  ```json
@@ -85,9 +87,9 @@ json = File.read('post.json')
85
87
  post = Post.new(json) #=> Ruson::NotNilException
86
88
  ```
87
89
 
88
- ### nested class
90
+ #### nested class
89
91
 
90
- #### class
92
+ ##### class
91
93
 
92
94
  post.json
93
95
  ```json
@@ -119,7 +121,7 @@ post = Post.new(json)
119
121
  post.picture.url #=> 'http://sample.com/picture.png'
120
122
  ```
121
123
 
122
- ##### Primary classes
124
+ ###### Primary classes
123
125
 
124
126
  * Boolean
125
127
  * Integer
@@ -151,7 +153,7 @@ post.view #=> 1234
151
153
  post.rate #=> 3.8
152
154
  ```
153
155
 
154
- #### each class
156
+ ##### each class
155
157
 
156
158
  post.json
157
159
  ```json
@@ -185,7 +187,7 @@ post = Post.new(json)
185
187
  post.tags.first.name #=> 'Ruby'
186
188
  ```
187
189
 
188
- ### enum
190
+ #### enum
189
191
 
190
192
  article.json
191
193
  ```json
@@ -213,7 +215,7 @@ article.status = 'undefined'
213
215
  #=> undefined is not a valid status (ArgumentError)
214
216
  ```
215
217
 
216
- ### to_json
218
+ #### to_json
217
219
 
218
220
  ```ruby
219
221
  class Post < Ruson::Base
@@ -228,7 +230,7 @@ post.url = 'https://example.com/examples'
228
230
  post.to_json #=> "{\"title\":\"Ruson\",\"url\":\"https://example.com/examples\"}"
229
231
  ```
230
232
 
231
- ### to_hash
233
+ #### to_hash
232
234
 
233
235
  ```ruby
234
236
  class Post < Ruson::Base
@@ -243,7 +245,7 @@ post.url = 'https://example.com/examples'
243
245
  post.to_hash #=> {title: "Ruson", url: "https://example.com/examples" }
244
246
  ```
245
247
 
246
- ### API json parser
248
+ #### API json parser
247
249
 
248
250
  ```ruby
249
251
  class Article < Ruson::Base
@@ -261,12 +263,127 @@ article = Article.new(response.body)
261
263
  article.title
262
264
  ```
263
265
 
266
+ ### Persistence
267
+
268
+ Persistence will save the models as JSON file, with the model ID as filename, and model class name as parent folder so that a `User` model with ID `1` will be saved as `Users/1.json`.
269
+
270
+ You *must* define the `output_folder` before to be able to call `save` or `destroy`.
271
+
272
+ ```ruby
273
+ Ruson.output_folder = './db/'
274
+ ```
275
+
276
+ #### Creating new record
277
+
278
+ ```ruby
279
+ class User < Ruson::Base
280
+ field :first_name
281
+ field :last_name
282
+ field :email
283
+ field :title
284
+ end
285
+
286
+ # Using new + save
287
+ guillaume = User.new(first_name: 'Guillaume', last_name: 'Briat', email: 'guillaume@kaamelott.fr')
288
+ guillaume.save # Creates the ./db/Users/1.json file
289
+
290
+ # Or using the create method
291
+ guillaume = User.create(first_name: 'Guillaume', last_name: 'Briat', email: 'guillaume@kaamelott.fr')
292
+
293
+ puts File.read('./db/Users/1.json')
294
+ {"first_name":"Guillaume","last_name":"Briat","email":"guillaume@kaamelott.fr"}
295
+ => nil
296
+ ```
297
+
298
+ #### Updating a record
299
+
300
+ ```ruby
301
+ # Assigning a value + save
302
+ guillaume.title = 'Burgundians King'
303
+ guillaume.save # Updates the ./db/Users/1.json file
304
+
305
+ # Or using the update method
306
+ guillaume.update(title: 'Burgundians King')
307
+
308
+ puts File.read('./db/Users/1.json')
309
+ {"first_name":"Guillaume","last_name":"Briat","email":"guillaume@kaamelott.fr","title":"Burgundians King"}
310
+ => nil
311
+ ```
312
+
313
+ #### Destroying a record
314
+
315
+ ```ruby
316
+ guillaume.destroy # Deletes the ./db/Users/1.json file
317
+
318
+ puts File.read('./db/Users/1.json')
319
+ Traceback (most recent call last):
320
+ 16: from /usr/local/bundle/gems/bundler-2.0.2/exe/bundle:30:in `block in <top (required)>'
321
+ ...
322
+ 2: from (irb):26
323
+ 1: from (irb):26:in `read'
324
+ Errno::ENOENT (No such file or directory @ rb_sysopen - ./db/Users/1.json)
325
+ ```
326
+
327
+ ### Querying
328
+
329
+ Ruson allows you to query for existing records.
330
+
331
+ You *must* define the `output_folder` before to query records.
332
+
333
+ ```ruby
334
+ Ruson.output_folder = './db/'
335
+ ```
336
+
337
+ #### Find a record by ID
338
+
339
+ ```ruby
340
+ user = User.find(1) # Searches for a ./db/Users/1.json file
341
+ ```
342
+
264
343
  ## Development
265
344
 
345
+ ### Without Docker
346
+
266
347
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
267
348
 
268
349
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
269
350
 
351
+ ### With Docker
352
+
353
+ ```
354
+ $ docker build -t `whoami`/ruson .
355
+ ```
356
+
357
+ In order to see the available Rake tasks:
358
+
359
+ ```
360
+ $ docker --rm `whoami`/ruson
361
+ rake build # Build ruson-1.2.0.gem into the pkg directory
362
+ rake clean # Remove any temporary products
363
+ rake clobber # Remove any generated files
364
+ rake install # Build and install ruson-1.2.0.gem into system gems
365
+ rake install:local # Build and install ruson-1.2.0.gem into system gems without network access
366
+ rake release[remote] # Create tag v1.2.0 and build and push ruson-1.2.0.gem to rubygems.org
367
+ rake spec # Run RSpec code examples
368
+ ```
369
+ _`--rm` means delete the container after the command ended._
370
+
371
+ In order to execute the tests:
372
+
373
+ ```
374
+ $ docker run --rm -it --volume "$PWD":/gem/ `whoami`/ruson rake spec
375
+ ```
376
+ _`--volume` is used to sync the files from your current folder into the container so that if you change a file, the modification is available in the container._
377
+
378
+ In the case you'd like to access the IRB console:
379
+
380
+ ```
381
+ $ docker run --rm -it --volume "$PWD":/gem/ `whoami`/ruson irb
382
+ irb(main):001:0> require 'ruson'
383
+ => true
384
+ irb(main):002:0>
385
+ ```
386
+
270
387
  ## Contributing
271
388
 
272
389
  Bug reports and pull requests are welcome on GitHub at https://github.com/klriutsa/ruson. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -2,5 +2,6 @@ require 'ruson/base'
2
2
  require 'ruson/version'
3
3
 
4
4
  module Ruson
5
-
5
+ # Output folder where Ruson will save models data
6
+ mattr_accessor :output_folder
6
7
  end
@@ -3,13 +3,16 @@ require 'active_support'
3
3
  require 'active_support/core_ext'
4
4
 
5
5
  require 'ruson/class/boolean'
6
- require 'ruson/class/integer'
7
6
  require 'ruson/class/float'
7
+ require 'ruson/class/integer'
8
+ require 'ruson/class/time'
8
9
 
9
10
  require 'ruson/converter'
10
11
  require 'ruson/json'
11
- require 'ruson/value'
12
12
  require 'ruson/nilable'
13
+ require 'ruson/persistence'
14
+ require 'ruson/querying'
15
+ require 'ruson/value'
13
16
 
14
17
  require 'ruson/error'
15
18
 
@@ -48,6 +51,8 @@ module Ruson
48
51
  end
49
52
 
50
53
  def add_accessor(name, options = {})
54
+ options[:name] = options[:name].try(:to_sym) if options[:name]
55
+
51
56
  @accessors ||= {}
52
57
  @accessors.merge!({ name.to_sym => options })
53
58
  end
@@ -55,8 +60,10 @@ module Ruson
55
60
 
56
61
  include Ruson::Converter
57
62
  include Ruson::Json
58
- include Ruson::Value
59
63
  include Ruson::Nilable
64
+ include Ruson::Persistence
65
+ include Ruson::Querying
66
+ include Ruson::Value
60
67
 
61
68
  def initialize(json, root_key: nil)
62
69
  params = get_hash_from_json(json)
@@ -66,7 +73,17 @@ module Ruson
66
73
  end
67
74
 
68
75
  def to_hash
69
- convert_to_hash(self.class.accessors)
76
+ res = convert_to_hash(self.class.accessors)
77
+
78
+ res.inject({}) do |result, attributes|
79
+ key, value = attributes
80
+ if self.class.accessors[key] && self.class.accessors[key].key?(:name)
81
+ result[self.class.accessors[key][:name].to_s] = value
82
+ else
83
+ result[key] = value
84
+ end
85
+ result
86
+ end
70
87
  end
71
88
 
72
89
  def to_json
@@ -76,18 +93,24 @@ module Ruson
76
93
  private
77
94
 
78
95
  def init_attributes(accessors, params)
96
+ update_attributes(accessors, params)
97
+
98
+ self.class.attr_accessor(:id)
99
+ set_attribute(:id, params[:id]) if params[:id]
100
+ end
101
+
102
+ def set_attribute(attr_name, val)
103
+ self.send("#{attr_name}=".to_sym, val)
104
+ end
105
+
106
+ def update_attributes(accessors, params)
79
107
  accessors.each do |key, options|
80
- key_name = options[:name] || key
81
- value = params[key_name]
108
+ value = params[options[:name]] || params[key]
82
109
 
83
110
  check_nilable(value, options)
84
111
  val = get_val(value, options)
85
112
  set_attribute(key, val)
86
113
  end
87
114
  end
88
-
89
- def set_attribute(attr_name, val)
90
- self.send("#{attr_name}=".to_sym, val)
91
- end
92
115
  end
93
116
  end
@@ -0,0 +1,12 @@
1
+ class Time
2
+ def self.new(value)
3
+ case value
4
+ when Integer, Float
5
+ Time.at(value).to_time
6
+ when String
7
+ Time.parse(value).to_time
8
+ else
9
+ value
10
+ end
11
+ end
12
+ end
@@ -1,7 +1,7 @@
1
1
  module Ruson
2
2
  module Converter
3
3
  def convert_to_hash(accessors)
4
- accessors.keys.inject({}) do |result, key|
4
+ accessors.keys.inject({ id: id }) do |result, key|
5
5
  value = send(key)
6
6
  result[key.to_sym] = convert_array_to_hash_value(value)
7
7
  result
@@ -1,4 +1,4 @@
1
1
  module Ruson
2
- class NotNilException < StandardError
3
- end
4
- end
2
+ class NotNilException < StandardError; end
3
+ class RecordNotFound < StandardError; end
4
+ end
@@ -0,0 +1,89 @@
1
+ module Ruson
2
+ module Persistence
3
+ def self.included(klass)
4
+ klass.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def create(*args)
9
+ new_record = new(*args)
10
+ new_record.save
11
+ new_record
12
+ end
13
+
14
+ def model_base_path
15
+ File.join(Ruson.output_folder, "#{self.name}s")
16
+ end
17
+ end
18
+
19
+ def delete_file_from_disk
20
+ FileUtils.rm_f(model_path)
21
+ end
22
+
23
+ def destroy
24
+ delete_file_from_disk
25
+ true
26
+ end
27
+
28
+ def ensure_output_folder_is_defined
29
+ return if Ruson.output_folder
30
+
31
+ raise ArgumentError, 'No output folder defined. You can define it ' \
32
+ 'using Ruson.output_folder = "/path/to/db/folder"'
33
+ end
34
+
35
+ def ensure_model_folder_exists
36
+ return if File.exist?(self.class.model_base_path)
37
+
38
+ FileUtils.mkdir_p(self.class.model_base_path)
39
+ end
40
+
41
+ def generate_uniq_id
42
+ ensure_model_folder_exists
43
+
44
+ return if id
45
+
46
+ id = 0
47
+
48
+ Dir.glob(File.join(self.class.model_base_path, '*.json')).each do |file|
49
+ file_id = File.basename(file, '.json').to_i
50
+
51
+ id = file_id if file_id > id
52
+ end
53
+
54
+ self.id = id + 1
55
+ end
56
+
57
+ def model_path
58
+ File.join(self.class.model_base_path, "#{id}.json")
59
+ end
60
+
61
+ def write_file_to_disk
62
+ File.open(model_path, 'w') do |file|
63
+ file.write(to_json)
64
+ end
65
+ end
66
+
67
+ def save
68
+ ensure_output_folder_is_defined
69
+ generate_uniq_id
70
+ write_file_to_disk
71
+ true
72
+ end
73
+
74
+ def update(attributes)
75
+ attributes.symbolize_keys!
76
+
77
+ # Takes only accessor for attributes to be updated, avoiding to nullify
78
+ # the other attributes.
79
+ filtered_accessors = self.class.accessors.select do |key, accessor_attrs|
80
+ attributes.key?(key) || attributes.key?(accessor_attrs[:name])
81
+ end
82
+
83
+ update_attributes(filtered_accessors, attributes)
84
+
85
+ save
86
+ end
87
+ end
88
+ end
89
+
@@ -0,0 +1,28 @@
1
+ module Ruson
2
+ module Querying
3
+ def self.included(klass)
4
+ klass.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def find(id)
9
+ file_path = File.join(model_base_path, "#{id}.json")
10
+
11
+ return unless File.exist?(file_path)
12
+
13
+ json = JSON.parse(File.read(file_path))
14
+ json[:id] = id
15
+
16
+ new json
17
+ end
18
+
19
+ def find!(id)
20
+ record = find(id)
21
+
22
+ raise Ruson::RecordNotFound unless record
23
+
24
+ record
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Ruson
2
- VERSION = '1.2.0'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -1,28 +1,31 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
2
+ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "ruson/version"
4
+ require 'ruson/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "ruson"
7
+ spec.name = 'ruson'
8
8
  spec.version = Ruson::VERSION
9
- spec.authors = ["klriutsa"]
10
- spec.email = ["hiroya.kurushima@litalico.co.jp"]
9
+ spec.authors = ['klriutsa']
10
+ spec.email = ['hiroya.kurushima@litalico.co.jp']
11
11
 
12
12
  spec.summary = %q{ruby json library.}
13
13
  spec.description = %q{ruby json library.}
14
- spec.homepage = "https://github.com/klriutsa/ruson"
15
- spec.license = "MIT"
14
+ spec.homepage = 'https://github.com/klriutsa/ruson'
15
+ spec.license = 'MIT'
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
18
  f.match(%r{^(test|spec|features)/})
19
19
  end
20
- spec.bindir = "exe"
20
+ spec.bindir = 'exe'
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
- spec.require_paths = ["lib"]
22
+ spec.require_paths = ['lib']
23
23
 
24
- spec.add_development_dependency "bundler", "~> 2.0"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
- spec.add_development_dependency "rspec", "~> 3.0"
27
24
  spec.add_dependency 'activesupport'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 2.0'
27
+ spec.add_development_dependency 'ffaker', '~> 2.13.0'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.0'
30
+ spec.add_development_dependency 'timecop', '~> 0.9.1'
28
31
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruson
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - klriutsa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-27 00:00:00.000000000 Z
11
+ date: 2019-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -24,6 +38,20 @@ dependencies:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
40
  version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ffaker
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.13.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.13.0
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rake
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -53,19 +81,19 @@ dependencies:
53
81
  - !ruby/object:Gem::Version
54
82
  version: '3.0'
55
83
  - !ruby/object:Gem::Dependency
56
- name: activesupport
84
+ name: timecop
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
- - - ">="
87
+ - - "~>"
60
88
  - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :runtime
89
+ version: 0.9.1
90
+ type: :development
63
91
  prerelease: false
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
- - - ">="
94
+ - - "~>"
67
95
  - !ruby/object:Gem::Version
68
- version: '0'
96
+ version: 0.9.1
69
97
  description: ruby json library.
70
98
  email:
71
99
  - hiroya.kurushima@litalico.co.jp
@@ -77,6 +105,7 @@ files:
77
105
  - ".rspec"
78
106
  - ".travis.yml"
79
107
  - CODE_OF_CONDUCT.md
108
+ - Dockerfile
80
109
  - Gemfile
81
110
  - LICENSE.txt
82
111
  - README.md
@@ -88,10 +117,13 @@ files:
88
117
  - lib/ruson/class/boolean.rb
89
118
  - lib/ruson/class/float.rb
90
119
  - lib/ruson/class/integer.rb
120
+ - lib/ruson/class/time.rb
91
121
  - lib/ruson/converter.rb
92
122
  - lib/ruson/error.rb
93
123
  - lib/ruson/json.rb
94
124
  - lib/ruson/nilable.rb
125
+ - lib/ruson/persistence.rb
126
+ - lib/ruson/querying.rb
95
127
  - lib/ruson/value.rb
96
128
  - lib/ruson/version.rb
97
129
  - ruson.gemspec
@@ -114,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
146
  - !ruby/object:Gem::Version
115
147
  version: '0'
116
148
  requirements: []
117
- rubygems_version: 3.0.3
149
+ rubygems_version: 3.0.6
118
150
  signing_key:
119
151
  specification_version: 4
120
152
  summary: ruby json library.