lazy_as_json 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2089ff5725a076fc31c149b22365a0c661c517ba
4
+ data.tar.gz: 8c018d3b697b855eda8d922fa4fe8f83ba11dee0
5
+ SHA512:
6
+ metadata.gz: 42a68a54d003a1863aff93dd434dca60b17c0390d4783e282a7d4a5309eb67934dd12b8807493d12dab3831a9c433035badb45bdf6f8f3af1af1d3eb7a5283de
7
+ data.tar.gz: 212626600b580f4ff5fbeb4e62f879c5fcf71094c9585434fcc72758824db30c9ed3108292f81d800c4c086ad8d523b00f8dc7e4e65cc84dc773e52b836a8500
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'bundler', '~> 1.11.2'
4
+
5
+ # Specify your gem's dependencies in lazy_as_json.gemspec
6
+ gemspec
@@ -0,0 +1,72 @@
1
+ # LazyAsJson
2
+
3
+ [![Circle CI](https://circleci.com/gh/we4tech/lazy-as-json.svg?style=svg)](https://circleci.com/gh/we4tech/lazy-as-json)
4
+
5
+ A simple and concise way to use as_json with “only”, “except” and other options without using them literally.
6
+
7
+ Instead of using this -
8
+
9
+ `User.as_json(only: [:id, :first_name, profiles: [:company, :location]])`
10
+
11
+ You can perhaps use this -
12
+
13
+ `User.as_json(only_keys: ‘_,first_name,profiles(p),p.company,p.location’)`
14
+
15
+ As simple as this.
16
+
17
+ You can control what your API response should include through a flexible parameter string.
18
+
19
+ i.e. - “/api/v1/users/me?_keys=_,last_name,profiles(p),p.company,p.location”
20
+
21
+ This parameter string could dig through the nested objects and their nesting too.
22
+ Just to reduce the API response size significantly, you can use this parameter control over wherever it is used.
23
+ However it might seems quite trivial but frankly speaking it saves lot in response data hence faster loading time at client side.
24
+
25
+ Moreover as it uses Hash.new and constructs attribute on runtime, you can throttle calling from the expensive method by using this parameter string.
26
+
27
+ ## How to Construct Attribute Filters ?
28
+
29
+ |Symbol|Description|
30
+ |---|---|
31
+ |_|It represents id as short cut|
32
+ |nested_objects(< name >)|It represents the alias for the nested object (it works with single or multiple values)|
33
+ |< alias_name >.< key >|It represents the specific attribute from the nested object|
34
+ |< alias_name >.< key >(< alias >)|It represents aliasing a nested object from a nested object and this list could go on and on.|
35
+
36
+ ### Example Usages
37
+
38
+ ```ruby
39
+ User.as_json(only_keys: '_,first_name,email,profiles(p),p._,p.company,p.address(pa),pa.city,pa.country(pac),pac.iso_code')
40
+ ```
41
+
42
+ Here, "_" represents `user_instance.id`, `profiles` represents `user_instance.profiles`, `p.address(pa)` represents `user_instance.profiles[].address` and finally `pa.country(pac)` represents `user_instance.profiles[].address.country` and so on.
43
+
44
+ ## Installation
45
+
46
+ Add this line to your application's Gemfile:
47
+
48
+ ```ruby
49
+ gem 'lazy_as_json'
50
+ ```
51
+
52
+ And then execute:
53
+
54
+ $ bundle
55
+
56
+ Or install it yourself as:
57
+
58
+ $ gem install lazy_as_json
59
+
60
+ ## Usage
61
+
62
+ TODO: Write usage instructions here
63
+
64
+ ## Development
65
+
66
+ 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.
67
+
68
+ 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).
69
+
70
+ ## Contributing
71
+
72
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/lazy_as_json.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "lazy_as_json"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,8 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.1
4
+
5
+ dependencies:
6
+ pre:
7
+ - if $(gem list bundler -v 1.9.5 -i); then gem uninstall -x -i /home/ubuntu/.rvm/gems/ruby-2.2.1@global bundler; fi
8
+ - gem install bundler -v "1.11.2"
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lazy_as_json/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'lazy_as_json'
8
+ spec.version = LazyAsJson::VERSION
9
+ spec.authors = ['nhm tanveer hossain khan']
10
+ spec.email = ['hasan83bd@gmail.com']
11
+
12
+ spec.summary = %q{Take control on what to return from the API response, define the attributes map in a short syntax over parameter}
13
+ spec.description = %q{Lazy As Json
14
+
15
+ A simple and concise way to use as_json with “only”, “except” and other options without using them literally.
16
+
17
+ Instead of using this -
18
+
19
+ `User.as_json(only: [:id, :first_name, profiles: [:company, :location]])`
20
+
21
+ You can perhaps use this -
22
+
23
+ `User.as_json(only_keys: ‘_,first_name,profiles(p),p.company,p.location’)`
24
+
25
+ As simple as this.
26
+
27
+ You can control what your API response should include through a flexible parameter string.
28
+
29
+ i.e. - “/api/v1/users/me?_keys=_,last_name,profiles(p),p.company,p.location”
30
+
31
+ This parameter string could dig through the nested objects and their nesting too.
32
+ Just to reduce the API response size significantly, you can use this parameter control over wherever it is used.
33
+ However it might seems quite trivial but frankly speaking it saves lot in response data hence faster loading time at client side.
34
+
35
+ Moreover as it uses Hash.new and constructs attribute on runtime, you can throttle calling from the expensive method by using this parameter string.
36
+ }
37
+ spec.homepage = 'http://hasan.wordpress.com'
38
+
39
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
40
+ # delete this section to allow pushing this gem to any host.
41
+ if spec.respond_to?(:metadata)
42
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
43
+ else
44
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
45
+ end
46
+
47
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
48
+ spec.bindir = "exe"
49
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
50
+ spec.require_paths = ["lib"]
51
+
52
+ spec.add_development_dependency "bundler", "~> 1.11"
53
+ spec.add_development_dependency "rake", "~> 10.0"
54
+ spec.add_development_dependency "rspec", "~> 3.0"
55
+ end
@@ -0,0 +1,5 @@
1
+ require 'lazy_as_json/version'
2
+
3
+ module LazyAsJson ; end
4
+
5
+ require 'lazy_as_json/attribute_filter'
@@ -0,0 +1,160 @@
1
+ module LazyAsJson
2
+ module AttributeFilter
3
+
4
+ def self.included(base)
5
+ base.respond_to?(:prepend) ?
6
+ base.prepend(OnlyKeysFilter) :
7
+ base.include(OnlyKeysFilter)
8
+ end
9
+
10
+ module OnlyKeysFilter
11
+ # The list of excluded attribute accessor name
12
+ BANNED_KEYS = [:all, :delete, :delete_all, :destroy_all, :destroy, :update, :save, :create,
13
+ :create!, :save!, :update_attributes, :update_attribute, :update_attributes!,
14
+ :find, :where, :increment, :increment!, :decrement, :decrement!, :remove,
15
+ :object_id]
16
+
17
+ # Returns the JSON serializable hash object with the data from the specified keys.
18
+ #
19
+ # It Looks up for :only_keys for enabling attribute throttling, in case of absense it returns the
20
+ # actual data from overrode 'as_json' method.
21
+ #
22
+ # @param opts [Hash] The additional options whether to throttle response attribute or not.
23
+ # @return [Hash] The generated has based on the trottled keys or based on the overrode as_json method
24
+ def as_json(opts = {})
25
+ __set_only_keys opts.delete(:only_keys)
26
+
27
+ __lazy_mode? ? build_as_json : super(opts)
28
+ end
29
+
30
+ # Sets the only keys, if you intend to use those directly instead of going through :as_json
31
+ #
32
+ # @return key_str [String] The attribute filtering string
33
+ def __set_only_keys(key_str)
34
+ @__only_keys = key_str.to_s.strip
35
+ @__only_keys = @__only_keys.empty? ? nil : @__only_keys
36
+ end
37
+
38
+ # Returns the applied attribute filtering string
39
+ #
40
+ # @return [String] The attribute filtering string
41
+ def __only_keys
42
+ @__only_keys
43
+ end
44
+
45
+ # Creates a Hash object which lazly assembles attributes from the object or any of their parents.
46
+ # However, it could be delegated from another object through exposing :source method.
47
+ #
48
+ # @return [Hash] The empty Hash object with the attribute builder closure
49
+ def lazy_as_json(opts = {})
50
+ Hash.new do |hash, key|
51
+ hash[key] = _with_lazy(_find_value(key))
52
+ end
53
+ end
54
+
55
+ # Returns true if the lazy mode is enabled through :only_keys option
56
+ #
57
+ # @return [Boolean] true if lazy mode is enabled
58
+ def __lazy_mode?
59
+ __only_keys && !__only_keys.empty?
60
+ end
61
+
62
+ # Builds the attributes hash object based on the applied attribute filtering string
63
+ #
64
+ # @return [Hash] The attributes based on the attribute filtering string
65
+ def build_as_json
66
+ @_af_references = {}
67
+ hash = lazy_as_json
68
+ keys = []
69
+
70
+ __only_keys.split(',').each do |key|
71
+ value = hash
72
+
73
+ for nested_key in find_full_key(key)
74
+ if Array === value
75
+ value = value.map { |v| v[nested_key] }.compact
76
+ elsif Hash === value
77
+ value = value[nested_key]
78
+ end
79
+ end
80
+ end
81
+
82
+ hash
83
+ end
84
+
85
+ # Return true if the specified attribute key is not banned
86
+ #
87
+ # @return [Boolean] True if not listed under *BANNED_KEYS*
88
+ def __allowed_attribute_key?(key)
89
+ !BANNED_KEYS.include?(key)
90
+ end
91
+
92
+ private
93
+
94
+ def _with_lazy(value)
95
+ if value.respond_to?(:lazy_as_json)
96
+ value.lazy_as_json
97
+ elsif Array === value
98
+ value.map { |v| v.respond_to?(:lazy_as_json) ? v.lazy_as_json : v }
99
+ else
100
+ value
101
+ end
102
+ end
103
+
104
+ def _find_value(key)
105
+ return unless __allowed_attribute_key?(key)
106
+
107
+ if respond_to?(key)
108
+ send(key)
109
+ elsif _source_declared? && source.respond_to?(key)
110
+ source.send(key)
111
+ end
112
+ end
113
+
114
+ def _source_declared?
115
+ @_source_declared ||= respond_to?(:source)
116
+ end
117
+
118
+ # Generate a fully qualified key based on it's parent object reference tree based
119
+ # on the shortest key name.
120
+ #
121
+ def find_full_key(key)
122
+ # Try to find 'actual_name(alias)' key formation
123
+ if (matched = key.match(/^(.+)\((\w+)\)$/))
124
+ prefixes = matched[1].split('.')
125
+ @_af_references[matched[2]] =
126
+ (wrap_array(prefixes[0, prefixes.size - 1]).
127
+ map { |prefix| @_af_references[prefix] } << prefixes.last.to_sym).
128
+ flatten
129
+ else
130
+ # Split key string based on the '.'
131
+ parts = key.split('.')
132
+
133
+ # Traverse through all prefixes and try to find if those are aliased or not aliased
134
+ # and build a complete reference tree.
135
+ prefixes = parts[0, parts.size - 1].map { |prefix| @_af_references[prefix] || [] }.flatten
136
+
137
+ # Take the actual key part
138
+ key_part = parts.last
139
+
140
+ # Construct the full reference tree and expand if short form is used.
141
+ suffix = if key_part == '_'
142
+ :id
143
+ elsif (matched = key_part.match(/^(.+)_$/))
144
+ :"#{matched[1]}_id"
145
+ else
146
+ key_part.to_sym
147
+ end
148
+
149
+ prefixes << suffix
150
+ end
151
+ end
152
+
153
+ def wrap_array(values)
154
+ return [] if values.nil?
155
+ return values if Array === values
156
+ [values]
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,3 @@
1
+ module LazyAsJson
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lazy_as_json
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - nhm tanveer hossain khan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-03-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: |
56
+ Lazy As Json
57
+
58
+ A simple and concise way to use as_json with “only”, “except” and other options without using them literally.
59
+
60
+ Instead of using this -
61
+
62
+ `User.as_json(only: [:id, :first_name, profiles: [:company, :location]])`
63
+
64
+ You can perhaps use this -
65
+
66
+ `User.as_json(only_keys: ‘_,first_name,profiles(p),p.company,p.location’)`
67
+
68
+ As simple as this.
69
+
70
+ You can control what your API response should include through a flexible parameter string.
71
+
72
+ i.e. - “/api/v1/users/me?_keys=_,last_name,profiles(p),p.company,p.location”
73
+
74
+ This parameter string could dig through the nested objects and their nesting too.
75
+ Just to reduce the API response size significantly, you can use this parameter control over wherever it is used.
76
+ However it might seems quite trivial but frankly speaking it saves lot in response data hence faster loading time at client side.
77
+
78
+ Moreover as it uses Hash.new and constructs attribute on runtime, you can throttle calling from the expensive method by using this parameter string.
79
+ email:
80
+ - hasan83bd@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - ".gitignore"
86
+ - ".rspec"
87
+ - ".travis.yml"
88
+ - Gemfile
89
+ - README.md
90
+ - Rakefile
91
+ - bin/console
92
+ - bin/setup
93
+ - circle.yml
94
+ - lazy_as_json.gemspec
95
+ - lib/lazy_as_json.rb
96
+ - lib/lazy_as_json/attribute_filter.rb
97
+ - lib/lazy_as_json/version.rb
98
+ homepage: http://hasan.wordpress.com
99
+ licenses: []
100
+ metadata:
101
+ allowed_push_host: https://rubygems.org
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.5.1
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Take control on what to return from the API response, define the attributes
122
+ map in a short syntax over parameter
123
+ test_files: []