dm-json-search 0.0.1

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 @@
1
+ pkg/*
data/README ADDED
@@ -0,0 +1,75 @@
1
+ Search your models with JSON!
2
+ =============================
3
+
4
+ How to use
5
+ ----------
6
+
7
+ gem install gemcutter # unless you already have
8
+ gem tumble # unless you already have
9
+ gem install dm-json-search
10
+
11
+ Then, in your model:
12
+
13
+ class Book
14
+ include JsonSearch::Searchable
15
+ end
16
+
17
+ Wala:
18
+
19
+ Book.search('[{"eql":{"title":"Psycho"}}]')
20
+ # Should this be mixed in automagically to every model?
21
+
22
+
23
+ Another example query and it's json
24
+ -----------------------------------
25
+
26
+ puts Ticket.select { |t|
27
+ t.title == "foo" || (t.user_id == 6 && t.account_id == 9)
28
+ }.query.to_json
29
+
30
+ Becomes:
31
+
32
+ {
33
+ "model": "Ticket",
34
+ "repository": "default",
35
+ "conditions": {
36
+ "and": [{
37
+ "or": [{
38
+ "eql": { "Ticket.title": "foo" }
39
+ },
40
+ {
41
+ "and": [{
42
+ "eql": { "Ticket.user_id": 6 }
43
+ },
44
+ {
45
+ "eql": { "Ticket.account_id": 9 }
46
+ }]
47
+ }]
48
+ }]
49
+ },
50
+ "reload": false,
51
+ "order": ["Ticket.id.asc"],
52
+ "fields": [...],
53
+ "links": [], // these don't work yet
54
+ "unique": false,
55
+ "limit": null,
56
+ "offset": 0
57
+ }
58
+
59
+
60
+ Explanation
61
+ -----------
62
+
63
+ Operations are objects with a key for the Operation#slug and an array of operands. Comparisons are object with a key for the Comparison#slug and an object with a property key and a value for the comparison.
64
+
65
+ So, you can send in a huge json string like the above, or you can just send in the conditions part: { "and": [...] }. Or, if you just send in an array, it will wrap that array in an "and" hash.
66
+
67
+ All DM Operations and Conditions are permitted on any Property of any Model. This probably needs to be changed to only search some fields.
68
+
69
+
70
+ TODO
71
+ ----
72
+
73
+ * Spec it out and make sure there are no vectors for destructive actions
74
+ * Make links work
75
+ * Make it better?
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "dm-json-search"
5
+ gemspec.summary = "Search your models with JSON!"
6
+ gemspec.description = "Use JSON and Hashes to create complex queries on your DM models."
7
+ gemspec.email = "nathan@myobie.com"
8
+ gemspec.homepage = "http://github.com/myobie/dm-json-search"
9
+ gemspec.authors = ["Nathan Herald"]
10
+ end
11
+ Jeweler::GemcutterTasks.new
12
+ rescue LoadError
13
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
14
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,44 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{dm-json-search}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Nathan Herald"]
12
+ s.date = %q{2009-10-22}
13
+ s.description = %q{Use JSON and Hashes to create complex queries on your DM models.}
14
+ s.email = %q{nathan@myobie.com}
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "dm-json-search.gemspec",
24
+ "lib/dm-json-search.rb",
25
+ "lib/dm-json-search/query_ext.rb",
26
+ "lib/dm-json-search/searchable.rb"
27
+ ]
28
+ s.homepage = %q{http://github.com/myobie/dm-json-search}
29
+ s.rdoc_options = ["--charset=UTF-8"]
30
+ s.require_paths = ["lib"]
31
+ s.rubygems_version = %q{1.3.5}
32
+ s.summary = %q{Search your models with JSON!}
33
+
34
+ if s.respond_to? :specification_version then
35
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
36
+ s.specification_version = 3
37
+
38
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
39
+ else
40
+ end
41
+ else
42
+ end
43
+ end
44
+
@@ -0,0 +1,3 @@
1
+ require 'dm-core'
2
+ require File.dirname(__FILE__) + '/dm-json-search/query_ext'
3
+ require File.dirname(__FILE__) + '/dm-json-search/searchable'
@@ -0,0 +1,161 @@
1
+ class DataMapper::Query
2
+
3
+ def to_hash
4
+ {
5
+ "repository" => self.repository.name,
6
+ "model" => self.model.name,
7
+ "fields" => self.fields.collect { |p| "#{p.model}.#{p.name}" },
8
+ "links" => [], # I need to see an example of this,
9
+ "order" => self.order.collect { |o| "#{o.target.model}.#{o.target.name}.#{o.operator}" },
10
+ "limit" => self.limit,
11
+ "offset" => self.offset,
12
+ "reload" => @reload,
13
+ "unique" => @unique,
14
+ "conditions" => operation_to_hash(self.conditions)
15
+ }
16
+ end
17
+
18
+ def to_json
19
+ to_hash.to_json
20
+ end
21
+
22
+ def all
23
+ repository.scope { model.all(self) }
24
+ end
25
+
26
+ class << self
27
+
28
+ def from_json(json)
29
+ from_hash(JSON.parse(json))
30
+ end
31
+
32
+ def from_hash(hash)
33
+
34
+ respository = hash["repository"].to_sym
35
+ model = string_to_model(hash["model"])
36
+ options = {}
37
+
38
+ # fields
39
+ unless hash["fields"].blank? || !hash["fields"].is_a?(Array)
40
+ options[:fields] = hash["fields"].collect { |f| string_to_property(f) }
41
+ end
42
+
43
+ # links, again, I don't know enough about, I've never used them
44
+
45
+ # order
46
+ unless hash["order"].blank? || !hash["order"].is_a?(Array)
47
+ options[:order] = hash["order"].collect { |p| string_to_order(p) }
48
+ end
49
+
50
+ options[:limit] = hash["limit"] unless hash["limit"].blank?
51
+ options[:offset] = hash["offset"] unless hash["offset"].blank?
52
+
53
+ unless hash["conditions"].blank?
54
+ options[:conditions] = string_to_conditions(hash["conditions"])
55
+ end
56
+
57
+ new(repository, model, options)
58
+
59
+ end#from_hash
60
+
61
+ private
62
+ def string_to_model(string)
63
+ DataMapper::Model.descendants.find { |m| m.name == string }
64
+ end
65
+
66
+ def string_to_property(string)
67
+ strings = string.split(".")
68
+
69
+ if model = string_to_model(strings[0])
70
+ model.properties[strings[1]]
71
+ else
72
+ nil
73
+ end
74
+ end
75
+
76
+ def string_to_relationship(string)
77
+ strings = string.split(".")
78
+
79
+ if model = string_to_model(strings[0])
80
+ model.relationships[strings[1]]
81
+ else
82
+ nil
83
+ end
84
+ end
85
+
86
+ def string_to_order(string)
87
+ property = string_to_property(string)
88
+ direction = string.split(".")[2] || :asc
89
+ DataMapper::Query::Direction.new(property, direction.to_sym)
90
+ end
91
+
92
+ def string_to_conditions(conditions)
93
+ if DataMapper::Query::Conditions::Operation.slugs.include?(conditions.keys.first.to_sym)
94
+ string_to_operation(conditions)
95
+ elsif DataMapper::Query::Conditions::Comparison.slugs.include?(conditions.keys.first.to_sym)
96
+ string_to_comparison(conditions)
97
+ else
98
+ nil
99
+ end
100
+ end
101
+
102
+ def string_to_operation(operation)
103
+ slug = operation.keys.first.to_sym
104
+ operands = operation.values.first.collect { |o| string_to_conditions(o) }
105
+ DataMapper::Query::Conditions::Operation.new(slug, *operands)
106
+ end
107
+
108
+ def string_to_comparison(comparison)
109
+ slug = comparison.keys.first.to_sym
110
+ compare = comparison.values.first
111
+ subject = string_to_subject(compare.keys.first)
112
+ value = compare.values.first
113
+ DataMapper::Query::Conditions::Comparison.new(slug, subject, value)
114
+ end
115
+
116
+ def string_to_subject(subject)
117
+ if property = string_to_property(subject)
118
+ property
119
+ else
120
+ string_to_relationship(subject)
121
+ end
122
+ end
123
+
124
+ end#class << self
125
+
126
+ private
127
+ def operation_to_hash(operation)
128
+ {
129
+ operation.slug => operation.operands.collect { |o| operand_to_hash(o) }
130
+ }
131
+ end
132
+
133
+ def operand_to_hash(operand)
134
+ # TODO: figure out a better way to detect what type of object it is
135
+
136
+ if operand.respond_to?(:subject) # it's a comparison
137
+ comparison_to_hash(operand)
138
+ elsif operand.respond_to?(:operands) # it's an operation
139
+ operation_to_hash(operand)
140
+ else # it must be some other kind of object
141
+ operand
142
+ end
143
+ end
144
+
145
+ def comparison_to_hash(comparison)
146
+ {
147
+ comparison.slug => {
148
+ subject_to_hash(comparison.subject) => comparison.value
149
+ }
150
+ }
151
+ end
152
+
153
+ def subject_to_hash(subject)
154
+ if subject.respond_to?(:parent_model)
155
+ "#{subject.source_model}.#{subject.name}"
156
+ else
157
+ "#{subject.model}.#{subject.name}"
158
+ end
159
+ end
160
+
161
+ end
@@ -0,0 +1,29 @@
1
+ module JsonSearch
2
+ module Searchable
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def search(json = {})
10
+ json = JSON.parse(json) if json.is_a?(String)
11
+
12
+ if json.is_a?(Array)
13
+ json = { "conditions" => { "and" => json } }
14
+ end
15
+
16
+ if json["conditions"].blank? && !json["and"].blank?
17
+ json = { "conditions" => json }
18
+ end
19
+
20
+ json["model"] = name
21
+ json["repository"] = default_repository_name
22
+
23
+ DataMapper::Query.from_hash(json).all
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ JSONSearch = JsonSearch
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-json-search
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nathan Herald
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-22 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Use JSON and Hashes to create complex queries on your DM models.
17
+ email: nathan@myobie.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - .gitignore
26
+ - README
27
+ - Rakefile
28
+ - VERSION
29
+ - dm-json-search.gemspec
30
+ - lib/dm-json-search.rb
31
+ - lib/dm-json-search/query_ext.rb
32
+ - lib/dm-json-search/searchable.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/myobie/dm-json-search
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --charset=UTF-8
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Search your models with JSON!
61
+ test_files: []
62
+