├── .gitignore ├── .rspec ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── autotest └── discovery.rb ├── lib ├── pathy.rb └── pathy │ └── version.rb ├── pathy.gemspec └── spec ├── lib └── pathy_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/* 2 | *.gem 3 | .bundle 4 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --format=nested 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | 4 | 5 | gem 'json' 6 | 7 | group :development do 8 | gem 'rspec' 9 | gem 'ZenTest' 10 | end 11 | 12 | # Specify your gem's dependencies in pathy.gemspec 13 | gemspec 14 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | pathy (0.0.1) 5 | 6 | GEM 7 | remote: http://rubygems.org/ 8 | specs: 9 | ZenTest (4.5.0) 10 | diff-lcs (1.1.2) 11 | json (1.5.1) 12 | rspec (2.5.0) 13 | rspec-core (~> 2.5.0) 14 | rspec-expectations (~> 2.5.0) 15 | rspec-mocks (~> 2.5.0) 16 | rspec-core (2.5.1) 17 | rspec-expectations (2.5.0) 18 | diff-lcs (~> 1.1.2) 19 | rspec-mocks (2.5.0) 20 | 21 | PLATFORMS 22 | ruby 23 | 24 | DEPENDENCIES 25 | ZenTest 26 | json 27 | pathy! 28 | rspec 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Christopher Burnett 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Pathy ### 2 | 3 | JSON validation helper. 4 | 5 | ### Installation ### 6 | 7 | gem install pathy 8 | 9 | In Rails 10 | 11 | gem 'pathy' 12 | 13 | ### Usage ### 14 | 15 | Activate pathy for all objects 16 | 17 | ```ruby 18 | Object.pathy! 19 | ``` 20 | 21 | This adds the convenience methods to any object 22 | 23 | ```ruby 24 | @obj = %[ 25 | { 26 | "string" : "barr", 27 | "number" : 1, 28 | "array" : [1,2,3], 29 | "hash" : {"one":{"two" : 2}} 30 | } 31 | ] 32 | 33 | puts @obj.at_json_path("number") 34 | => 1 35 | puts @obj.at_json_path('array') 36 | => [1,2,3] 37 | @obj.at_json_path('hash.one') 38 | => {'two' => 2} 39 | ``` 40 | 41 | ###RSpec Matcher### 42 | 43 | ```ruby 44 | it "should work as rspec matcher" do 45 | @obj.should have_json_path "hash.one" 46 | end 47 | ``` 48 | 49 | 50 | ##Note on Patches/Pull Requests 51 | 52 | * Fork the project. 53 | * Make your feature addition or bug fix. 54 | * Add tests for it. This is important so I don't break it in a 55 | future version unintentionally. 56 | * Commit, do not mess with rakefile, version, or history. 57 | (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) 58 | * Send me a pull request. Bonus points for topic branches. 59 | 60 | ## Copyright 61 | 62 | Copyright (c) 2011 Christopher Burnett. See LICENSE for details. 63 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | -------------------------------------------------------------------------------- /autotest/discovery.rb: -------------------------------------------------------------------------------- 1 | Autotest.add_discovery { "rspec2" } 2 | -------------------------------------------------------------------------------- /lib/pathy.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | module Pathy 4 | 5 | InvalidPathError = Class.new(NoMethodError) 6 | 7 | module InstanceMethods 8 | 9 | # returns the value parsed from JSON at a given path. 10 | # Example: 11 | # {:some_key => {:nested_key => 'awesome'}}.at_json_path('some_key.nested_key') 12 | # returns 'awesome' 13 | def at_json_path path 14 | path.split('.').inject( reparsed_from_json ) do |reparsed_self, method| 15 | 16 | is_array_like = reparsed_self.respond_to?(:push) 17 | key_or_index = is_array_like ? method.to_i : method 18 | has_key_or_index = is_array_like ? !reparsed_self.slice(key_or_index).nil? : reparsed_self.keys.include?(key_or_index) 19 | 20 | raise InvalidPathError, "Could not resolve #{path} at #{key_or_index}" unless has_key_or_index 21 | 22 | reparsed_self.send '[]', key_or_index 23 | end 24 | end 25 | 26 | # returns true if the path is found. Provides usage in Rspec 27 | # Example in rspec: 28 | # {:some_key => {:nested_key => 'awesome'}}.should have_json_path('some_key.nested_key') 29 | def has_json_path? path 30 | begin 31 | at_json_path path 32 | true 33 | rescue InvalidPathError 34 | false 35 | end 36 | end 37 | 38 | # returns the parsed JSON representation of an instance. 39 | # If the current instance is a string we assume it's JSON 40 | def reparsed_from_json 41 | self.is_a?(String) ? JSON.parse(self) : JSON.parse(self.to_json) 42 | end 43 | end 44 | module ClassMethods 45 | 46 | # adds pathy methods to a Class 47 | def pathy! 48 | self.send :include, InstanceMethods 49 | end 50 | end 51 | end 52 | 53 | Object.extend Pathy::ClassMethods 54 | 55 | -------------------------------------------------------------------------------- /lib/pathy/version.rb: -------------------------------------------------------------------------------- 1 | module Pathy 2 | VERSION = "0.0.4" 3 | end 4 | -------------------------------------------------------------------------------- /pathy.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "pathy/version" 4 | 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "pathy" 8 | s.version = Pathy::VERSION 9 | s.platform = Gem::Platform::RUBY 10 | s.authors = ["Christopher Burnett"] 11 | s.email = ["signalstatic@gmail.com"] 12 | s.homepage = "http://rubygems.org/gems/pathy" 13 | s.summary = %q{JSON Validation Helper} 14 | s.description = %q{Simple JSON Validation and rspec matchers} 15 | 16 | s.rubyforge_project = "pathy" 17 | 18 | s.add_dependency "json", "~> 1.4" 19 | 20 | s.files = `git ls-files`.split("\n") 21 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 22 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 23 | s.require_paths = ["lib"] 24 | end 25 | -------------------------------------------------------------------------------- /spec/lib/pathy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Pathy do 4 | 5 | describe "pathy scope" do 6 | 7 | describe "Object.new" do 8 | it "should not respond to :has_json_path?" do 9 | Object.new.should_not respond_to(:has_json_path?) 10 | end 11 | end 12 | 13 | describe "SampleClass" do 14 | before :all do 15 | SampleClass.pathy! 16 | end 17 | 18 | it "should not respond to :has_json_path?" do 19 | Object.new.should_not respond_to(:has_json_path?) 20 | end 21 | 22 | it "should respond to :has_json_path?" do 23 | SampleClass.new.should respond_to(:has_json_path?) 24 | end 25 | 26 | end 27 | 28 | end 29 | 30 | before :all do 31 | 32 | @json = %[ 33 | { 34 | "string" : "barr", 35 | "number" : 1, 36 | "array" : [1,2,3], 37 | "hash" : {"one":{"two" : 2}}, 38 | "bool" : false, 39 | "nullval" : null 40 | } 41 | ] 42 | 43 | @json_array = %[ 44 | [{ 45 | "string" : "barr", 46 | "number" : 1, 47 | "array" : [1,2,3], 48 | "hash" : {"one":{"two" : 2}} 49 | }] 50 | ] 51 | 52 | end 53 | 54 | 55 | describe "for hashes" do 56 | before :all do 57 | Object.pathy! 58 | 59 | @obj = JSON.parse(@json) 60 | @array = JSON.parse(@json_array) 61 | end 62 | 63 | it "should parse 'number' as 1" do 64 | @obj.at_json_path("number").should == 1 65 | end 66 | 67 | it "should parse 'array' as [1,2,3]" do 68 | @obj.at_json_path('array').should == [1,2,3] 69 | end 70 | 71 | it "should parse 'hash.one' as {'two' => 2}" do 72 | @json.at_json_path('hash.one').should == {'two' => 2} 73 | end 74 | it "should parse 'hash.one' as {'two': 2}" do 75 | @obj.at_json_path('hash.one.two').should == 2 76 | end 77 | 78 | describe "invalid paths" do 79 | it "should raise InvalidPathError" do 80 | lambda { 81 | @obj.at_json_path('foo.bar') 82 | }.should raise_error Pathy::InvalidPathError 83 | end 84 | end 85 | 86 | describe "#has_json_path?" do 87 | it "should be true for valid paths" do 88 | @obj.has_json_path?('hash.one.two').should be_true 89 | end 90 | 91 | it "should have 'bool'" do 92 | @obj.should have_json_path 'bool' 93 | end 94 | 95 | it "should have 'nullval'" do 96 | @obj.should have_json_path 'nullval' 97 | end 98 | 99 | it "should be false for invalid paths" do 100 | @obj.has_json_path?('hash.one.does_not_exist').should be_false 101 | end 102 | 103 | it "should work as rspec matcher" do 104 | @obj.should have_json_path "hash.one" 105 | end 106 | 107 | end 108 | 109 | 110 | end 111 | 112 | describe "for arrays" do 113 | 114 | before :all do 115 | @array = JSON.parse(@json_array) 116 | end 117 | 118 | it "should find the index" do 119 | @array.at_json_path('0.hash.one.two').should == 2 120 | end 121 | 122 | end 123 | 124 | describe "for json strings" do 125 | 126 | it "should parse 'number' as 1" do 127 | @json.at_json_path("number").should == 1 128 | end 129 | 130 | it "should parse 'array' as [1,2,3]" do 131 | @json.at_json_path('array').should == [1,2,3] 132 | end 133 | 134 | it "should parse 'hash.one' as {'two' => 2}" do 135 | @json.at_json_path('hash.one').should == {'two' => 2} 136 | end 137 | 138 | it "should parse 'hash.one.two' as 2" do 139 | @json.at_json_path('hash.one.two').should == 2 140 | end 141 | 142 | end 143 | 144 | end 145 | 146 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 2 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 3 | require 'pathy' 4 | require 'json' 5 | 6 | class SampleClass 7 | 8 | end 9 | --------------------------------------------------------------------------------