dm-json-search 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+