ruson 1.2.0 → 1.3.0

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