├── .gitignore ├── .hound.yml ├── .rspec ├── .ruby-style.yml ├── .travis.yml ├── Appraisals ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── gemfiles ├── rails3.gemfile ├── rails4_0.gemfile ├── rails4_1.gemfile └── rails4_2.gemfile ├── lib ├── generators │ └── rails-push-notifications │ │ ├── USAGE │ │ ├── migrations_generator.rb │ │ └── templates │ │ └── migrations │ │ ├── create_rails_push_notifications_apps.rb │ │ └── create_rails_push_notifications_notifications.rb ├── rails-push-notifications.rb └── rails-push-notifications │ ├── apps.rb │ ├── apps │ ├── apns_app.rb │ ├── base_app.rb │ ├── gcm_app.rb │ └── mpns_app.rb │ ├── notification.rb │ ├── rails_push_notifications_railtie.rb │ └── version.rb ├── rails-push-notifications.gemspec └── spec ├── apps ├── apns_app_spec.rb ├── gcm_app_spec.rb └── mpns_app_spec.rb ├── factories ├── apps.rb └── notifications.rb ├── generators └── migrations_generator_spec.rb ├── notification_spec.rb ├── rails_apps ├── rails3_2.rb └── rails4.rb ├── spec_helper.rb └── support └── factory_girl.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | .DS_Store 15 | log/* 16 | spec/log/* 17 | # YARD artifacts 18 | .yardoc 19 | _yardoc 20 | doc/ 21 | gemfiles/*.lock 22 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | ruby: 2 | config_file: .ruby-style.yml 3 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.ruby-style.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Include: 3 | - "**/*.gemspec" 4 | - "**/*.podspec" 5 | - "**/*.jbuilder" 6 | - "**/*.rake" 7 | - "**/*.opal" 8 | - "**/Gemfile" 9 | - "**/Rakefile" 10 | - "**/Capfile" 11 | - "**/Guardfile" 12 | - "**/Podfile" 13 | - "**/Thorfile" 14 | - "**/Vagrantfile" 15 | - "**/Berksfile" 16 | - "**/Cheffile" 17 | - "**/Vagabondfile" 18 | Exclude: 19 | - "vendor/**/*" 20 | - "db/schema.rb" 21 | RunRailsCops: false 22 | DisplayCopNames: false 23 | StyleGuideCopsOnly: false 24 | Style/AccessModifierIndentation: 25 | Description: Check indentation of private/protected visibility modifiers. 26 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected 27 | Enabled: true 28 | EnforcedStyle: indent 29 | SupportedStyles: 30 | - outdent 31 | - indent 32 | Style/AlignHash: 33 | Description: Align the elements of a hash literal if they span more than one line. 34 | Enabled: true 35 | EnforcedHashRocketStyle: key 36 | EnforcedColonStyle: key 37 | EnforcedLastArgumentHashStyle: always_inspect 38 | SupportedLastArgumentHashStyles: 39 | - always_inspect 40 | - always_ignore 41 | - ignore_implicit 42 | - ignore_explicit 43 | Style/AlignParameters: 44 | Description: Align the parameters of a method call if they span more than one line. 45 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-double-indent 46 | Enabled: true 47 | EnforcedStyle: with_first_parameter 48 | SupportedStyles: 49 | - with_first_parameter 50 | - with_fixed_indentation 51 | Style/AndOr: 52 | Description: Use &&/|| instead of and/or. 53 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-and-or-or 54 | Enabled: true 55 | EnforcedStyle: always 56 | SupportedStyles: 57 | - always 58 | - conditionals 59 | Style/BarePercentLiterals: 60 | Description: Checks if usage of %() or %Q() matches configuration. 61 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand 62 | Enabled: true 63 | EnforcedStyle: bare_percent 64 | SupportedStyles: 65 | - percent_q 66 | - bare_percent 67 | Style/BracesAroundHashParameters: 68 | Description: Enforce braces style around hash parameters. 69 | Enabled: true 70 | EnforcedStyle: no_braces 71 | SupportedStyles: 72 | - braces 73 | - no_braces 74 | - context_dependent 75 | Style/CaseIndentation: 76 | Description: Indentation of when in a case/when/[else/]end. 77 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#indent-when-to-case 78 | Enabled: true 79 | IndentWhenRelativeTo: case 80 | SupportedStyles: 81 | - case 82 | - end 83 | IndentOneStep: false 84 | Style/ClassAndModuleChildren: 85 | Description: Checks style of children classes and modules. 86 | Enabled: false 87 | EnforcedStyle: nested 88 | SupportedStyles: 89 | - nested 90 | - compact 91 | Style/ClassCheck: 92 | Description: Enforces consistent use of `Object#is_a?` or `Object#kind_of?`. 93 | Enabled: true 94 | EnforcedStyle: is_a? 95 | SupportedStyles: 96 | - is_a? 97 | - kind_of? 98 | Style/CollectionMethods: 99 | Description: Preferred collection methods. 100 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size 101 | Enabled: true 102 | PreferredMethods: 103 | collect: map 104 | collect!: map! 105 | find: detect 106 | find_all: select 107 | reduce: inject 108 | Style/CommentAnnotation: 109 | Description: Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, 110 | REVIEW). 111 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#annotate-keywords 112 | Enabled: false 113 | Keywords: 114 | - TODO 115 | - FIXME 116 | - OPTIMIZE 117 | - HACK 118 | - REVIEW 119 | Style/DotPosition: 120 | Description: Checks the position of the dot in multi-line method calls. 121 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains 122 | Enabled: true 123 | EnforcedStyle: trailing 124 | SupportedStyles: 125 | - leading 126 | - trailing 127 | Style/EmptyLineBetweenDefs: 128 | Description: Use empty lines between defs. 129 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods 130 | Enabled: true 131 | AllowAdjacentOneLineDefs: false 132 | Style/EmptyLinesAroundBlockBody: 133 | Description: Keeps track of empty lines around block bodies. 134 | Enabled: true 135 | EnforcedStyle: no_empty_lines 136 | SupportedStyles: 137 | - empty_lines 138 | - no_empty_lines 139 | Style/EmptyLinesAroundClassBody: 140 | Description: Keeps track of empty lines around class bodies. 141 | Enabled: false 142 | EnforcedStyle: no_empty_lines 143 | SupportedStyles: 144 | - empty_lines 145 | - no_empty_lines 146 | Style/EmptyLinesAroundModuleBody: 147 | Description: Keeps track of empty lines around module bodies. 148 | Enabled: false 149 | EnforcedStyle: no_empty_lines 150 | SupportedStyles: 151 | - empty_lines 152 | - no_empty_lines 153 | Style/Encoding: 154 | Description: Use UTF-8 as the source file encoding. 155 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#utf-8 156 | Enabled: false 157 | EnforcedStyle: always 158 | SupportedStyles: 159 | - when_needed 160 | - always 161 | Style/FileName: 162 | Description: Use snake_case for source file names. 163 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files 164 | Enabled: false 165 | Exclude: [] 166 | Style/FirstParameterIndentation: 167 | Description: Checks the indentation of the first parameter in a method call. 168 | Enabled: true 169 | EnforcedStyle: special_for_inner_method_call_in_parentheses 170 | SupportedStyles: 171 | - consistent 172 | - special_for_inner_method_call 173 | - special_for_inner_method_call_in_parentheses 174 | Style/For: 175 | Description: Checks use of for or each in multiline loops. 176 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-for-loops 177 | Enabled: true 178 | EnforcedStyle: each 179 | SupportedStyles: 180 | - for 181 | - each 182 | Style/FormatString: 183 | Description: Enforce the use of Kernel#sprintf, Kernel#format or String#%. 184 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#sprintf 185 | Enabled: false 186 | EnforcedStyle: format 187 | SupportedStyles: 188 | - format 189 | - sprintf 190 | - percent 191 | Style/GlobalVars: 192 | Description: Do not introduce global variables. 193 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#instance-vars 194 | Enabled: false 195 | AllowedVariables: [] 196 | Style/GuardClause: 197 | Description: Check for conditionals that can be replaced with guard clauses 198 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals 199 | Enabled: false 200 | MinBodyLength: 1 201 | Style/HashSyntax: 202 | Description: 'Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax { :a => 203 | 1, :b => 2 }.' 204 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-literals 205 | Enabled: true 206 | EnforcedStyle: ruby19 207 | SupportedStyles: 208 | - ruby19 209 | - hash_rockets 210 | Style/IfUnlessModifier: 211 | Description: Favor modifier if/unless usage when you have a single-line body. 212 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier 213 | Enabled: false 214 | MaxLineLength: 80 215 | Style/IndentationWidth: 216 | Description: Use 2 spaces for indentation. 217 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation 218 | Enabled: true 219 | Width: 2 220 | Style/IndentHash: 221 | Description: Checks the indentation of the first key in a hash literal. 222 | Enabled: true 223 | EnforcedStyle: special_inside_parentheses 224 | SupportedStyles: 225 | - special_inside_parentheses 226 | - consistent 227 | Style/LambdaCall: 228 | Description: Use lambda.call(...) instead of lambda.(...). 229 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc-call 230 | Enabled: false 231 | EnforcedStyle: call 232 | SupportedStyles: 233 | - call 234 | - braces 235 | Style/Next: 236 | Description: Use `next` to skip iteration instead of a condition at the end. 237 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals 238 | Enabled: false 239 | EnforcedStyle: skip_modifier_ifs 240 | MinBodyLength: 3 241 | SupportedStyles: 242 | - skip_modifier_ifs 243 | - always 244 | Style/NonNilCheck: 245 | Description: Checks for redundant nil checks. 246 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks 247 | Enabled: true 248 | IncludeSemanticChanges: false 249 | Style/MethodDefParentheses: 250 | Description: Checks if the method definitions have or don't have parentheses. 251 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-parens 252 | Enabled: true 253 | EnforcedStyle: require_parentheses 254 | SupportedStyles: 255 | - require_parentheses 256 | - require_no_parentheses 257 | Style/MethodName: 258 | Description: Use the configured style when naming methods. 259 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars 260 | Enabled: true 261 | EnforcedStyle: snake_case 262 | SupportedStyles: 263 | - snake_case 264 | - camelCase 265 | Style/MultilineOperationIndentation: 266 | Description: Checks indentation of binary operations that span more than one line. 267 | Enabled: true 268 | EnforcedStyle: aligned 269 | SupportedStyles: 270 | - aligned 271 | - indented 272 | Style/NumericLiterals: 273 | Description: Add underscores to large numeric literals to improve their readability. 274 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics 275 | Enabled: false 276 | MinDigits: 5 277 | Style/ParenthesesAroundCondition: 278 | Description: Don't use parentheses around the condition of an if/unless/while. 279 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-parens-if 280 | Enabled: true 281 | AllowSafeAssignment: true 282 | Style/PercentLiteralDelimiters: 283 | Description: Use `%`-literal delimiters consistently 284 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces 285 | Enabled: false 286 | PreferredDelimiters: 287 | "%": "()" 288 | "%i": "()" 289 | "%q": "()" 290 | "%Q": "()" 291 | "%r": "{}" 292 | "%s": "()" 293 | "%w": "()" 294 | "%W": "()" 295 | "%x": "()" 296 | Style/PercentQLiterals: 297 | Description: Checks if uses of %Q/%q match the configured preference. 298 | Enabled: true 299 | EnforcedStyle: lower_case_q 300 | SupportedStyles: 301 | - lower_case_q 302 | - upper_case_q 303 | Style/PredicateName: 304 | Description: Check the names of predicate methods. 305 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark 306 | Enabled: true 307 | NamePrefix: 308 | - is_ 309 | - has_ 310 | - have_ 311 | NamePrefixBlacklist: 312 | - is_ 313 | Style/RaiseArgs: 314 | Description: Checks the arguments passed to raise/fail. 315 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages 316 | Enabled: false 317 | EnforcedStyle: exploded 318 | SupportedStyles: 319 | - compact 320 | - exploded 321 | Style/RedundantReturn: 322 | Description: Don't use return where it's not required. 323 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-explicit-return 324 | Enabled: true 325 | AllowMultipleReturnValues: false 326 | Style/RegexpLiteral: 327 | Description: Use %r for regular expressions matching more than `MaxSlashes` '/' 328 | characters. Use %r only for regular expressions matching more than `MaxSlashes` 329 | '/' character. 330 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-r 331 | Enabled: false 332 | MaxSlashes: 1 333 | Style/Semicolon: 334 | Description: Don't use semicolons to terminate expressions. 335 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon 336 | Enabled: true 337 | AllowAsExpressionSeparator: false 338 | Style/SignalException: 339 | Description: Checks for proper usage of fail and raise. 340 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method 341 | Enabled: false 342 | EnforcedStyle: semantic 343 | SupportedStyles: 344 | - only_raise 345 | - only_fail 346 | - semantic 347 | Style/SingleLineBlockParams: 348 | Description: Enforces the names of some block params. 349 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks 350 | Enabled: false 351 | Methods: 352 | - reduce: 353 | - a 354 | - e 355 | - inject: 356 | - a 357 | - e 358 | Style/SingleLineMethods: 359 | Description: Avoid single-line methods. 360 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods 361 | Enabled: false 362 | AllowIfMethodIsEmpty: true 363 | Style/StringLiterals: 364 | Description: Checks if uses of quotes match the configured preference. 365 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals 366 | Enabled: true 367 | EnforcedStyle: single_quotes 368 | SupportedStyles: 369 | - single_quotes 370 | - double_quotes 371 | Style/StringLiteralsInInterpolation: 372 | Description: Checks if uses of quotes inside expressions in interpolated strings 373 | match the configured preference. 374 | Enabled: true 375 | EnforcedStyle: single_quotes 376 | SupportedStyles: 377 | - single_quotes 378 | - double_quotes 379 | Style/SpaceAroundBlockParameters: 380 | Description: Checks the spacing inside and after block parameters pipes. 381 | Enabled: true 382 | EnforcedStyleInsidePipes: no_space 383 | SupportedStyles: 384 | - space 385 | - no_space 386 | Style/SpaceAroundEqualsInParameterDefault: 387 | Description: Checks that the equals signs in parameter default assignments have 388 | or don't have surrounding space depending on configuration. 389 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-around-equals 390 | Enabled: true 391 | EnforcedStyle: space 392 | SupportedStyles: 393 | - space 394 | - no_space 395 | Style/SpaceBeforeBlockBraces: 396 | Description: Checks that the left block brace has or doesn't have space before it. 397 | Enabled: true 398 | EnforcedStyle: space 399 | SupportedStyles: 400 | - space 401 | - no_space 402 | Style/SpaceInsideBlockBraces: 403 | Description: Checks that block braces have or don't have surrounding space. For 404 | blocks taking parameters, checks that the left brace has or doesn't have trailing 405 | space. 406 | Enabled: true 407 | EnforcedStyle: space 408 | SupportedStyles: 409 | - space 410 | - no_space 411 | EnforcedStyleForEmptyBraces: no_space 412 | SpaceBeforeBlockParameters: true 413 | Style/SpaceInsideHashLiteralBraces: 414 | Description: Use spaces inside hash literal braces - or don't. 415 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 416 | Enabled: true 417 | EnforcedStyle: space 418 | EnforcedStyleForEmptyBraces: no_space 419 | SupportedStyles: 420 | - space 421 | - no_space 422 | Style/SymbolProc: 423 | Description: Use symbols as procs instead of blocks when possible. 424 | Enabled: true 425 | IgnoredMethods: 426 | - respond_to 427 | Style/TrailingBlankLines: 428 | Description: Checks trailing blank lines and final newline. 429 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#newline-eof 430 | Enabled: true 431 | EnforcedStyle: final_newline 432 | SupportedStyles: 433 | - final_newline 434 | - final_blank_line 435 | Style/TrailingComma: 436 | Description: Checks for trailing comma in parameter lists and literals. 437 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas 438 | Enabled: false 439 | EnforcedStyleForMultiline: no_comma 440 | SupportedStyles: 441 | - comma 442 | - no_comma 443 | Style/TrivialAccessors: 444 | Description: Prefer attr_* methods to trivial readers/writers. 445 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr_family 446 | Enabled: false 447 | ExactNameMatch: false 448 | AllowPredicates: false 449 | AllowDSLWriters: false 450 | Whitelist: 451 | - to_ary 452 | - to_a 453 | - to_c 454 | - to_enum 455 | - to_h 456 | - to_hash 457 | - to_i 458 | - to_int 459 | - to_io 460 | - to_open 461 | - to_path 462 | - to_proc 463 | - to_r 464 | - to_regexp 465 | - to_str 466 | - to_s 467 | - to_sym 468 | Style/VariableName: 469 | Description: Use the configured style when naming variables. 470 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars 471 | Enabled: true 472 | EnforcedStyle: snake_case 473 | SupportedStyles: 474 | - snake_case 475 | - camelCase 476 | Style/WhileUntilModifier: 477 | Description: Favor modifier while/until usage when you have a single-line body. 478 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier 479 | Enabled: false 480 | MaxLineLength: 80 481 | Style/WordArray: 482 | Description: Use %w or %W for arrays of words. 483 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-w 484 | Enabled: false 485 | MinSize: 0 486 | WordRegex: !ruby/regexp /\A[\p{Word}]+\z/ 487 | Metrics/AbcSize: 488 | Description: A calculated magnitude based on number of assignments, branches, and 489 | conditions. 490 | Enabled: true 491 | Max: 15 492 | Metrics/BlockNesting: 493 | Description: Avoid excessive block nesting 494 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count 495 | Enabled: false 496 | Max: 3 497 | Metrics/ClassLength: 498 | Description: Avoid classes longer than 100 lines of code. 499 | Enabled: false 500 | CountComments: false 501 | Max: 100 502 | Metrics/CyclomaticComplexity: 503 | Description: A complexity metric that is strongly correlated to the number of test 504 | cases needed to validate a method. 505 | Enabled: false 506 | Max: 6 507 | Metrics/LineLength: 508 | Description: Limit lines to 80 characters. 509 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits 510 | Enabled: true 511 | Max: 80 512 | AllowURI: true 513 | URISchemes: 514 | - http 515 | - https 516 | Metrics/MethodLength: 517 | Description: Avoid methods longer than 10 lines of code. 518 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods 519 | Enabled: false 520 | CountComments: false 521 | Max: 10 522 | Metrics/ParameterLists: 523 | Description: Avoid parameter lists longer than three or four parameters. 524 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params 525 | Enabled: false 526 | Max: 5 527 | CountKeywordArgs: true 528 | Metrics/PerceivedComplexity: 529 | Description: A complexity metric geared towards measuring complexity for a human 530 | reader. 531 | Enabled: false 532 | Max: 7 533 | Lint/AssignmentInCondition: 534 | Description: Don't use assignment in conditions. 535 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition 536 | Enabled: false 537 | AllowSafeAssignment: true 538 | Lint/EndAlignment: 539 | Description: Align ends correctly. 540 | Enabled: true 541 | AlignWith: keyword 542 | SupportedStyles: 543 | - keyword 544 | - variable 545 | Lint/DefEndAlignment: 546 | Description: Align ends corresponding to defs correctly. 547 | Enabled: true 548 | AlignWith: start_of_line 549 | SupportedStyles: 550 | - start_of_line 551 | - def 552 | Rails/ActionFilter: 553 | Description: Enforces consistent use of action filter methods. 554 | Enabled: false 555 | EnforcedStyle: action 556 | SupportedStyles: 557 | - action 558 | - filter 559 | Include: 560 | - app/controllers/**/*.rb 561 | Rails/DefaultScope: 562 | Description: Checks if the argument passed to default_scope is a block. 563 | Enabled: true 564 | Include: 565 | - app/models/**/*.rb 566 | Rails/HasAndBelongsToMany: 567 | Description: Prefer has_many :through to has_and_belongs_to_many. 568 | Enabled: true 569 | Include: 570 | - app/models/**/*.rb 571 | Rails/Output: 572 | Description: Checks for calls to puts, print, etc. 573 | Enabled: true 574 | Include: 575 | - app/**/*.rb 576 | - config/**/*.rb 577 | - db/**/*.rb 578 | - lib/**/*.rb 579 | Rails/ReadWriteAttribute: 580 | Description: Checks for read_attribute(:attr) and write_attribute(:attr, val). 581 | Enabled: true 582 | Include: 583 | - app/models/**/*.rb 584 | Rails/ScopeArgs: 585 | Description: Checks the arguments of ActiveRecord scopes. 586 | Enabled: true 587 | Include: 588 | - app/models/**/*.rb 589 | Rails/Validation: 590 | Description: Use validates :attribute, hash of validations. 591 | Enabled: true 592 | Include: 593 | - app/models/**/*.rb 594 | Style/InlineComment: 595 | Description: Avoid inline comments. 596 | Enabled: false 597 | Style/MethodCalledOnDoEndBlock: 598 | Description: Avoid chaining a method call on a do...end block. 599 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks 600 | Enabled: false 601 | Style/SymbolArray: 602 | Description: Use %i or %I for arrays of symbols. 603 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-i 604 | Enabled: false 605 | Style/ExtraSpacing: 606 | Description: Do not use unnecessary spacing. 607 | Enabled: true 608 | Style/AccessorMethodName: 609 | Description: Check the naming of accessor methods for get_/set_. 610 | Enabled: false 611 | Style/Alias: 612 | Description: Use alias_method instead of alias. 613 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method 614 | Enabled: false 615 | Style/AlignArray: 616 | Description: Align the elements of an array literal if they span more than one line. 617 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays 618 | Enabled: true 619 | Style/ArrayJoin: 620 | Description: Use Array#join instead of Array#*. 621 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#array-join 622 | Enabled: false 623 | Style/AsciiComments: 624 | Description: Use only ascii symbols in comments. 625 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-comments 626 | Enabled: false 627 | Style/AsciiIdentifiers: 628 | Description: Use only ascii symbols in identifiers. 629 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-identifiers 630 | Enabled: false 631 | Style/Attr: 632 | Description: Checks for uses of Module#attr. 633 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr 634 | Enabled: false 635 | Style/BeginBlock: 636 | Description: Avoid the use of BEGIN blocks. 637 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks 638 | Enabled: true 639 | Style/BlockComments: 640 | Description: Do not use block comments. 641 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-block-comments 642 | Enabled: true 643 | Style/BlockEndNewline: 644 | Description: Put end statement of multiline block on its own line. 645 | Enabled: true 646 | Style/Blocks: 647 | Description: Avoid using {...} for multi-line blocks (multiline chaining is always 648 | ugly). Prefer {...} over do...end for single-line blocks. 649 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks 650 | Enabled: true 651 | Style/CaseEquality: 652 | Description: Avoid explicit use of the case equality operator(===). 653 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-case-equality 654 | Enabled: false 655 | Style/CharacterLiteral: 656 | Description: Checks for uses of character literals. 657 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-character-literals 658 | Enabled: false 659 | Style/ClassAndModuleCamelCase: 660 | Description: Use CamelCase for classes and modules. 661 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#camelcase-classes 662 | Enabled: true 663 | Style/ClassMethods: 664 | Description: Use self when defining module/class methods. 665 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#def-self-singletons 666 | Enabled: true 667 | Style/ClassVars: 668 | Description: Avoid the use of class variables. 669 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-class-vars 670 | Enabled: false 671 | Style/ColonMethodCall: 672 | Description: 'Do not use :: for method call.' 673 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#double-colons 674 | Enabled: false 675 | Style/CommentIndentation: 676 | Description: Indentation of comments. 677 | Enabled: true 678 | Style/ConstantName: 679 | Description: Constants should use SCREAMING_SNAKE_CASE. 680 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#screaming-snake-case 681 | Enabled: true 682 | Style/DefWithParentheses: 683 | Description: Use def with parentheses when there are arguments. 684 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-parens 685 | Enabled: true 686 | Style/DeprecatedHashMethods: 687 | Description: Checks for use of deprecated Hash methods. 688 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-key 689 | Enabled: false 690 | Style/Documentation: 691 | Description: Document classes and non-namespace modules. 692 | Enabled: false 693 | Style/DoubleNegation: 694 | Description: Checks for uses of double negation (!!). 695 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang 696 | Enabled: false 697 | Style/EachWithObject: 698 | Description: Prefer `each_with_object` over `inject` or `reduce`. 699 | Enabled: false 700 | Style/ElseAlignment: 701 | Description: Align elses and elsifs correctly. 702 | Enabled: true 703 | Style/EmptyElse: 704 | Description: Avoid empty else-clauses. 705 | Enabled: true 706 | Style/EmptyLines: 707 | Description: Don't use several empty lines in a row. 708 | Enabled: true 709 | Style/EmptyLinesAroundAccessModifier: 710 | Description: Keep blank lines around access modifiers. 711 | Enabled: true 712 | Style/EmptyLinesAroundMethodBody: 713 | Description: Keeps track of empty lines around method bodies. 714 | Enabled: true 715 | Style/EmptyLiteral: 716 | Description: Prefer literals to Array.new/Hash.new/String.new. 717 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash 718 | Enabled: false 719 | Style/EndBlock: 720 | Description: Avoid the use of END blocks. 721 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-END-blocks 722 | Enabled: true 723 | Style/EndOfLine: 724 | Description: Use Unix-style line endings. 725 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#crlf 726 | Enabled: true 727 | Style/EvenOdd: 728 | Description: Favor the use of Fixnum#even? && Fixnum#odd? 729 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods 730 | Enabled: false 731 | Style/FlipFlop: 732 | Description: Checks for flip flops 733 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-flip-flops 734 | Enabled: false 735 | Style/IfWithSemicolon: 736 | Description: Do not use if x; .... Use the ternary operator instead. 737 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs 738 | Enabled: false 739 | Style/IndentationConsistency: 740 | Description: Keep indentation straight. 741 | Enabled: true 742 | Style/IndentArray: 743 | Description: Checks the indentation of the first element in an array literal. 744 | Enabled: true 745 | Style/InfiniteLoop: 746 | Description: Use Kernel#loop for infinite loops. 747 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#infinite-loop 748 | Enabled: true 749 | Style/Lambda: 750 | Description: Use the new lambda literal syntax for single-line blocks. 751 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#lambda-multi-line 752 | Enabled: false 753 | Style/LeadingCommentSpace: 754 | Description: Comments should start with a space. 755 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-space 756 | Enabled: true 757 | Style/LineEndConcatenation: 758 | Description: Use \ instead of + or << to concatenate two string literals at line 759 | end. 760 | Enabled: false 761 | Style/MethodCallParentheses: 762 | Description: Do not use parentheses for method calls with no arguments. 763 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-args-no-parens 764 | Enabled: true 765 | Style/ModuleFunction: 766 | Description: Checks for usage of `extend self` in modules. 767 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function 768 | Enabled: false 769 | Style/MultilineBlockChain: 770 | Description: Avoid multi-line chains of blocks. 771 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks 772 | Enabled: true 773 | Style/MultilineBlockLayout: 774 | Description: Ensures newlines after multiline block do statements. 775 | Enabled: true 776 | Style/MultilineIfThen: 777 | Description: Do not use then for multi-line if/unless. 778 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-then 779 | Enabled: true 780 | Style/MultilineTernaryOperator: 781 | Description: 'Avoid multi-line ?: (the ternary operator); use if/unless instead.' 782 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary 783 | Enabled: true 784 | Style/NegatedIf: 785 | Description: Favor unless over if for negative conditions (or control flow or). 786 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#unless-for-negatives 787 | Enabled: false 788 | Style/NegatedWhile: 789 | Description: Favor until over while for negative conditions. 790 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#until-for-negatives 791 | Enabled: false 792 | Style/NestedTernaryOperator: 793 | Description: Use one expression per branch in a ternary operator. 794 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-ternary 795 | Enabled: true 796 | Style/NilComparison: 797 | Description: Prefer x.nil? to x == nil. 798 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods 799 | Enabled: false 800 | Style/Not: 801 | Description: Use ! instead of not. 802 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#bang-not-not 803 | Enabled: false 804 | Style/OneLineConditional: 805 | Description: Favor the ternary operator(?:) over if/then/else/end constructs. 806 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator 807 | Enabled: false 808 | Style/OpMethod: 809 | Description: When defining binary operators, name the argument other. 810 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#other-arg 811 | Enabled: false 812 | Style/PerlBackrefs: 813 | Description: Avoid Perl-style regex back references. 814 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers 815 | Enabled: false 816 | Style/Proc: 817 | Description: Use proc instead of Proc.new. 818 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc 819 | Enabled: false 820 | Style/RedundantBegin: 821 | Description: Don't use begin blocks when they are not needed. 822 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#begin-implicit 823 | Enabled: true 824 | Style/RedundantException: 825 | Description: Checks for an obsolete RuntimeException argument in raise/fail. 826 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror 827 | Enabled: true 828 | Style/RedundantSelf: 829 | Description: Don't use self where it's not needed. 830 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-self-unless-required 831 | Enabled: true 832 | Style/RescueModifier: 833 | Description: Avoid using rescue in its modifier form. 834 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers 835 | Enabled: true 836 | Style/SelfAssignment: 837 | Description: Checks for places where self-assignment shorthand should have been 838 | used. 839 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#self-assignment 840 | Enabled: false 841 | Style/SingleSpaceBeforeFirstArg: 842 | Description: Checks that exactly one space is used between a method name and the 843 | first argument for method calls without parentheses. 844 | Enabled: true 845 | Style/SpaceAfterColon: 846 | Description: Use spaces after colons. 847 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 848 | Enabled: true 849 | Style/SpaceAfterComma: 850 | Description: Use spaces after commas. 851 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 852 | Enabled: true 853 | Style/SpaceAfterControlKeyword: 854 | Description: Use spaces after if/elsif/unless/while/until/case/when. 855 | Enabled: true 856 | Style/SpaceAfterMethodName: 857 | Description: Do not put a space between a method name and the opening parenthesis 858 | in a method definition. 859 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces 860 | Enabled: true 861 | Style/SpaceAfterNot: 862 | Description: Tracks redundant space after the ! operator. 863 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-bang 864 | Enabled: true 865 | Style/SpaceAfterSemicolon: 866 | Description: Use spaces after semicolons. 867 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 868 | Enabled: true 869 | Style/SpaceBeforeComma: 870 | Description: No spaces before commas. 871 | Enabled: true 872 | Style/SpaceBeforeComment: 873 | Description: Checks for missing space between code and a comment on the same line. 874 | Enabled: true 875 | Style/SpaceBeforeSemicolon: 876 | Description: No spaces before semicolons. 877 | Enabled: true 878 | Style/SpaceAroundOperators: 879 | Description: Use spaces around operators. 880 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators 881 | Enabled: true 882 | Style/SpaceBeforeModifierKeyword: 883 | Description: Put a space before the modifier keyword. 884 | Enabled: true 885 | Style/SpaceInsideBrackets: 886 | Description: No spaces after [ or before ]. 887 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-spaces-braces 888 | Enabled: true 889 | Style/SpaceInsideParens: 890 | Description: No spaces after ( or before ). 891 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-spaces-braces 892 | Enabled: true 893 | Style/SpaceInsideRangeLiteral: 894 | Description: No spaces inside range literals. 895 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals 896 | Enabled: true 897 | Style/SpecialGlobalVars: 898 | Description: Avoid Perl-style global variables. 899 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms 900 | Enabled: false 901 | Style/StructInheritance: 902 | Description: Checks for inheritance from Struct.new. 903 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new 904 | Enabled: true 905 | Style/Tab: 906 | Description: No hard tabs. 907 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation 908 | Enabled: true 909 | Style/TrailingWhitespace: 910 | Description: Avoid trailing whitespace. 911 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace 912 | Enabled: true 913 | Style/UnlessElse: 914 | Description: Do not use unless with else. Rewrite these with the positive case first. 915 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-else-with-unless 916 | Enabled: true 917 | Style/UnneededCapitalW: 918 | Description: Checks for %W when interpolation is not needed. 919 | Enabled: true 920 | Style/UnneededPercentQ: 921 | Description: Checks for %q/%Q when single quotes or double quotes would do. 922 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-q 923 | Enabled: true 924 | Style/UnneededPercentX: 925 | Description: Checks for %x when `` would do. 926 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-x 927 | Enabled: true 928 | Style/VariableInterpolation: 929 | Description: Don't interpolate global, instance and class variables directly in 930 | strings. 931 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate 932 | Enabled: false 933 | Style/WhenThen: 934 | Description: Use when x then ... for one-line cases. 935 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases 936 | Enabled: false 937 | Style/WhileUntilDo: 938 | Description: Checks for redundant do after while or until. 939 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do 940 | Enabled: true 941 | Lint/AmbiguousOperator: 942 | Description: Checks for ambiguous operators in the first argument of a method invocation 943 | without parentheses. 944 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-as-args 945 | Enabled: false 946 | Lint/AmbiguousRegexpLiteral: 947 | Description: Checks for ambiguous regexp literals in the first argument of a method 948 | invocation without parenthesis. 949 | Enabled: false 950 | Lint/BlockAlignment: 951 | Description: Align block ends correctly. 952 | Enabled: true 953 | Lint/ConditionPosition: 954 | Description: Checks for condition placed in a confusing position relative to the 955 | keyword. 956 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#same-line-condition 957 | Enabled: false 958 | Lint/Debugger: 959 | Description: Check for debugger calls. 960 | Enabled: true 961 | Lint/DeprecatedClassMethods: 962 | Description: Check for deprecated class method calls. 963 | Enabled: false 964 | Lint/DuplicateMethods: 965 | Description: Check for duplicate methods calls. 966 | Enabled: true 967 | Lint/ElseLayout: 968 | Description: Check for odd code arrangement in an else block. 969 | Enabled: false 970 | Lint/EmptyEnsure: 971 | Description: Checks for empty ensure block. 972 | Enabled: true 973 | Lint/EmptyInterpolation: 974 | Description: Checks for empty string interpolation. 975 | Enabled: true 976 | Lint/EndInMethod: 977 | Description: END blocks should not be placed inside method definitions. 978 | Enabled: true 979 | Lint/EnsureReturn: 980 | Description: Do not use return in an ensure block. 981 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-return-ensure 982 | Enabled: true 983 | Lint/Eval: 984 | Description: The use of eval represents a serious security risk. 985 | Enabled: true 986 | Lint/HandleExceptions: 987 | Description: Don't suppress exception. 988 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions 989 | Enabled: false 990 | Lint/InvalidCharacterLiteral: 991 | Description: Checks for invalid character literals with a non-escaped whitespace 992 | character. 993 | Enabled: false 994 | Lint/LiteralInCondition: 995 | Description: Checks of literals used in conditions. 996 | Enabled: false 997 | Lint/LiteralInInterpolation: 998 | Description: Checks for literals used in interpolation. 999 | Enabled: false 1000 | Lint/Loop: 1001 | Description: Use Kernel#loop with break rather than begin/end/until or begin/end/while 1002 | for post-loop tests. 1003 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#loop-with-break 1004 | Enabled: false 1005 | Lint/ParenthesesAsGroupedExpression: 1006 | Description: Checks for method calls with a space before the opening parenthesis. 1007 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces 1008 | Enabled: false 1009 | Lint/RequireParentheses: 1010 | Description: Use parentheses in the method call to avoid confusion about precedence. 1011 | Enabled: false 1012 | Lint/RescueException: 1013 | Description: Avoid rescuing the Exception class. 1014 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-blind-rescues 1015 | Enabled: true 1016 | Lint/ShadowingOuterLocalVariable: 1017 | Description: Do not use the same name as outer local variable for block arguments 1018 | or block local variables. 1019 | Enabled: true 1020 | Lint/SpaceBeforeFirstArg: 1021 | Description: Put a space between a method name and the first argument in a method 1022 | call without parentheses. 1023 | Enabled: true 1024 | Lint/StringConversionInInterpolation: 1025 | Description: Checks for Object#to_s usage in string interpolation. 1026 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-to-s 1027 | Enabled: true 1028 | Lint/UnderscorePrefixedVariableName: 1029 | Description: Do not use prefix `_` for a variable that is used. 1030 | Enabled: false 1031 | Lint/UnusedBlockArgument: 1032 | Description: Checks for unused block arguments. 1033 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars 1034 | Enabled: true 1035 | Lint/UnusedMethodArgument: 1036 | Description: Checks for unused method arguments. 1037 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars 1038 | Enabled: true 1039 | Lint/UnreachableCode: 1040 | Description: Unreachable code. 1041 | Enabled: true 1042 | Lint/UselessAccessModifier: 1043 | Description: Checks for useless access modifiers. 1044 | Enabled: true 1045 | Lint/UselessAssignment: 1046 | Description: Checks for useless assignment to a local variable. 1047 | StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars 1048 | Enabled: true 1049 | Lint/UselessComparison: 1050 | Description: Checks for comparison of something with itself. 1051 | Enabled: true 1052 | Lint/UselessElseWithoutRescue: 1053 | Description: Checks for useless `else` in `begin..end` without `rescue`. 1054 | Enabled: true 1055 | Lint/UselessSetterCall: 1056 | Description: Checks for useless setter call to a local variable. 1057 | Enabled: true 1058 | Lint/Void: 1059 | Description: Possible use of operator/literal/variable in void context. 1060 | Enabled: false 1061 | Rails/Delegate: 1062 | Description: Prefer delegate method for delegations. 1063 | Enabled: false 1064 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - "2.2.0" 4 | - "2.1.5" 5 | - "2.0.0" 6 | - "1.9.3" 7 | env: CODECLIMATE_REPO_TOKEN=a71858029824dc27376b83df5f2d38ab31758505c28267332ac411ca6f4d3335 8 | gemfile: 9 | - gemfiles/rails3.gemfile 10 | - gemfiles/rails4_0.gemfile 11 | - gemfiles/rails4_1.gemfile 12 | - gemfiles/rails4_2.gemfile 13 | script: "bundle exec rspec" 14 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise 'rails4_0' do 2 | gem 'rails', '~> 4.0.0' 3 | end 4 | 5 | appraise 'rails4_1' do 6 | gem 'rails', '~> 4.1.0' 7 | end 8 | 9 | appraise 'rails4_2' do 10 | gem 'rails', '~> 4.2.0' 11 | end 12 | 13 | appraise 'rails3' do 14 | gem 'rails', '~> 3.2.0' 15 | gem 'test-unit' 16 | end 17 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Declare your gem's dependencies in ror_push_notifications.gemspec. 4 | # Bundler will treat runtime dependencies like base dependencies, and 5 | # development dependencies will be added by default to the :development group. 6 | gemspec 7 | 8 | gem "codeclimate-test-reporter", group: :test, require: nil 9 | gem "appraisal" 10 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | rails-push-notifications (0.2.3) 5 | rails (>= 3.2) 6 | ruby-push-notifications (~> 0.3) 7 | 8 | GEM 9 | remote: http://rubygems.org/ 10 | specs: 11 | actionmailer (4.2.1) 12 | actionpack (= 4.2.1) 13 | actionview (= 4.2.1) 14 | activejob (= 4.2.1) 15 | mail (~> 2.5, >= 2.5.4) 16 | rails-dom-testing (~> 1.0, >= 1.0.5) 17 | actionpack (4.2.1) 18 | actionview (= 4.2.1) 19 | activesupport (= 4.2.1) 20 | rack (~> 1.6) 21 | rack-test (~> 0.6.2) 22 | rails-dom-testing (~> 1.0, >= 1.0.5) 23 | rails-html-sanitizer (~> 1.0, >= 1.0.1) 24 | actionview (4.2.1) 25 | activesupport (= 4.2.1) 26 | builder (~> 3.1) 27 | erubis (~> 2.7.0) 28 | rails-dom-testing (~> 1.0, >= 1.0.5) 29 | rails-html-sanitizer (~> 1.0, >= 1.0.1) 30 | activejob (4.2.1) 31 | activesupport (= 4.2.1) 32 | globalid (>= 0.3.0) 33 | activemodel (4.2.1) 34 | activesupport (= 4.2.1) 35 | builder (~> 3.1) 36 | activerecord (4.2.1) 37 | activemodel (= 4.2.1) 38 | activesupport (= 4.2.1) 39 | arel (~> 6.0) 40 | activesupport (4.2.1) 41 | i18n (~> 0.7) 42 | json (~> 1.7, >= 1.7.7) 43 | minitest (~> 5.1) 44 | thread_safe (~> 0.3, >= 0.3.4) 45 | tzinfo (~> 1.1) 46 | addressable (2.3.8) 47 | appraisal (2.0.1) 48 | activesupport (>= 3.2.21) 49 | bundler 50 | rake 51 | thor (>= 0.14.0) 52 | arel (6.0.0) 53 | builder (3.2.2) 54 | codeclimate-test-reporter (0.4.7) 55 | simplecov (>= 0.7.1, < 1.0.0) 56 | crack (0.4.2) 57 | safe_yaml (~> 1.0.0) 58 | diff-lcs (1.2.5) 59 | docile (1.1.5) 60 | erubis (2.7.0) 61 | factory_girl (4.5.0) 62 | activesupport (>= 3.0.0) 63 | generator_spec (0.9.3) 64 | activesupport (>= 3.0.0) 65 | railties (>= 3.0.0) 66 | globalid (0.3.5) 67 | activesupport (>= 4.1.0) 68 | i18n (0.7.0) 69 | json (1.8.2) 70 | loofah (2.0.2) 71 | nokogiri (>= 1.5.9) 72 | mail (2.6.3) 73 | mime-types (>= 1.16, < 3) 74 | mime-types (2.6.1) 75 | mini_portile (0.6.2) 76 | minitest (5.6.1) 77 | nokogiri (1.6.6.2) 78 | mini_portile (~> 0.6.0) 79 | rack (1.6.1) 80 | rack-test (0.6.3) 81 | rack (>= 1.0) 82 | rails (4.2.1) 83 | actionmailer (= 4.2.1) 84 | actionpack (= 4.2.1) 85 | actionview (= 4.2.1) 86 | activejob (= 4.2.1) 87 | activemodel (= 4.2.1) 88 | activerecord (= 4.2.1) 89 | activesupport (= 4.2.1) 90 | bundler (>= 1.3.0, < 2.0) 91 | railties (= 4.2.1) 92 | sprockets-rails 93 | rails-deprecated_sanitizer (1.0.3) 94 | activesupport (>= 4.2.0.alpha) 95 | rails-dom-testing (1.0.6) 96 | activesupport (>= 4.2.0.beta, < 5.0) 97 | nokogiri (~> 1.6.0) 98 | rails-deprecated_sanitizer (>= 1.0.1) 99 | rails-html-sanitizer (1.0.2) 100 | loofah (~> 2.0) 101 | railties (4.2.1) 102 | actionpack (= 4.2.1) 103 | activesupport (= 4.2.1) 104 | rake (>= 0.8.7) 105 | thor (>= 0.18.1, < 2.0) 106 | rake (10.4.2) 107 | rspec (3.2.0) 108 | rspec-core (~> 3.2.0) 109 | rspec-expectations (~> 3.2.0) 110 | rspec-mocks (~> 3.2.0) 111 | rspec-core (3.2.3) 112 | rspec-support (~> 3.2.0) 113 | rspec-expectations (3.2.1) 114 | diff-lcs (>= 1.2.0, < 2.0) 115 | rspec-support (~> 3.2.0) 116 | rspec-mocks (3.2.1) 117 | diff-lcs (>= 1.2.0, < 2.0) 118 | rspec-support (~> 3.2.0) 119 | rspec-rails (3.2.1) 120 | actionpack (>= 3.0, < 4.3) 121 | activesupport (>= 3.0, < 4.3) 122 | railties (>= 3.0, < 4.3) 123 | rspec-core (~> 3.2.0) 124 | rspec-expectations (~> 3.2.0) 125 | rspec-mocks (~> 3.2.0) 126 | rspec-support (~> 3.2.0) 127 | rspec-support (3.2.2) 128 | ruby-push-notifications (0.3.1) 129 | builder (~> 3.0) 130 | safe_yaml (1.0.4) 131 | simplecov (0.10.0) 132 | docile (~> 1.1.0) 133 | json (~> 1.8) 134 | simplecov-html (~> 0.10.0) 135 | simplecov-html (0.10.0) 136 | sprockets (3.1.0) 137 | rack (~> 1.0) 138 | sprockets-rails (2.3.1) 139 | actionpack (>= 3.0) 140 | activesupport (>= 3.0) 141 | sprockets (>= 2.8, < 4.0) 142 | sqlite3 (1.3.10) 143 | thor (0.19.1) 144 | thread_safe (0.3.5) 145 | tzinfo (1.2.2) 146 | thread_safe (~> 0.1) 147 | webmock (1.21.0) 148 | addressable (>= 2.3.6) 149 | crack (>= 0.3.2) 150 | 151 | PLATFORMS 152 | ruby 153 | 154 | DEPENDENCIES 155 | appraisal 156 | codeclimate-test-reporter 157 | factory_girl (~> 4.0) 158 | generator_spec (~> 0.9) 159 | rails-push-notifications! 160 | rspec (~> 3.2) 161 | rspec-rails (~> 3.0) 162 | sqlite3 (~> 1.3) 163 | webmock (~> 1.20) 164 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 YOURNAME 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 | # Rails Push Notifications 2 | [![Build Status](https://travis-ci.org/calonso/rails-push-notifications.svg)](https://travis-ci.org/calonso/rails-push-notifications) [![Code Climate](https://codeclimate.com/github/calonso/rails-push-notifications/badges/gpa.svg)](https://codeclimate.com/github/calonso/rails-push-notifications) [![Test Coverage](https://codeclimate.com/github/calonso/rails-push-notifications/badges/coverage.svg)](https://codeclimate.com/github/calonso/rails-push-notifications/coverage) 3 | ## Professional iOS, Android and Windows Phone push notifications for Ruby on Rails 4 | 5 | RailsPushNotifications is an intuitive and easy to use gem that will allow you to easily add Push Notifications to your project. 6 | 7 | RailsPushNotifications key features: 8 | 9 | * Multiple Apple/Android/WinPhone applications/configurations 10 | * Single and Bulk notifications 11 | * Fully customizable notification's contents 12 | * Detailed feedback on each individual notification's push results. 13 | 14 | ## Live example 15 | 16 | Want to try this gem live? 17 | 18 | Browse [https://rails-push-notifications-test.herokuapp.com](https://rails-push-notifications-test.herokuapp.com) and play with it! 19 | The source code for that project is here: [https://github.com/calonso/rails_push_notifications_test](https://github.com/calonso/rails_push_notifications_test) 20 | 21 | ## Installation 22 | To install the gem simply add this line to your Gemfile 23 | 24 | gem 'rails-push-notifications', '~> 0.2.0' 25 | 26 | and then install it by running 27 | 28 | $ bundle install 29 | 30 | Once you have it installed, you need to generate the database migrations in order to build the required database structure and then run the newly generated migrations. So do it by executing: 31 | 32 | $ rails generate rails_push_notifications:migrations 33 | $ rake db:migrate 34 | 35 | ## Applications setup 36 | 37 | First step when using RailsPushNotifications is to setup Apple/Android/WinPhone applications to which the notifications will be associated to. Each platform has it's own details and process. 38 | 39 | ### Apple Applications setup 40 | 41 | Once you have configured your app as Apple Push Notifications Service enabled and you have requested and downloaded both the development and production certificates, it's time to convert them to a readable format (.pem) to import them into Rpn afterwards. 42 | 43 | To convert the certificates the first thing you need to do is to export them as .p12 files. To do this: 44 | 45 | 1. Open Keychain Application of your Macintosh computer 46 | 2. Find the recently generated certificates. Click the disclosure button on the left of your certificate and select both the certificate and the contained key 47 | 3. Right click and select 'Export 2 items...'. Then select the desired .p12 format and save it to your disk 48 | 49 | Once exported, to convert it to the readable .pem format simply run: 50 | 51 | $ openssl pkcs12 -in -out .pem -nodes -clcerts 52 | 53 | Now it's time to create our applications 54 | 55 | app = RailsPushNotifications::APNSApp.new 56 | app.apns_dev_cert = File.read('path/to/your/development/certificate.pem') 57 | app.apns_prod_cert = File.read('path/to/your/production/certificate.pem') 58 | app.sandbox_mode = true 59 | app.save 60 | 61 | And that's it!! You can create as many different applications as you want. RailsPushNotifications gem allows it. 62 | 63 | ### Android Applications setup 64 | 65 | The only thing you need is the API_KEY that you will get from the Google APIS Console > API Access 66 | 67 | app = RailsPushNotifications::GCMApp.new 68 | app.gcm_key = '' 69 | app.save 70 | 71 | That's all. You have your applications' configurations stored in RailsPushNotification. 72 | 73 | ### WindowsPhone Applications setup 74 | 75 | This case is similar but even simpler than APNS Apps 76 | 77 | app = RailsPushNotifications::MPNSApp.new 78 | app.cert = File.read('path/to/your/certificate.pem') 79 | app.save 80 | 81 | ## Creating notifications 82 | 83 | The notifications interface is common to all kind of apps, but depending on the type of underlying app 84 | the destinations and the data format will vary, so for example 85 | 86 | ### Apple notifications 87 | 88 | app = 89 | notification = app.notifications.create( 90 | destinations: [ 91 | 'Your first destination token', 92 | 'Your second destination token' 93 | ], 94 | data: { aps: { alert: 'Hello APNS World!', sound: 'true', badge: 1 } } 95 | ) 96 | 97 | ### GCM Notifications 98 | 99 | app = 100 | notification = app.notifications.create( 101 | destinations: [ 102 | 'Your first destination token', 103 | 'Your second destination token' 104 | ], 105 | data: { message: 'Hello GCM World!' } 106 | ) 107 | 108 | ### WindowsPhone Notifications 109 | 110 | app = 111 | notification = app.notifications.create( 112 | destinations: [ 113 | 'Your first destination url', 114 | 'Your second destination url' 115 | ], 116 | data: { title: 'Title', message: 'Hello MPNS World!', type: :toast } 117 | ) 118 | 119 | ## Pushing notifications 120 | 121 | Eventually we want to actually push our notifications. For that task all the different apps 122 | have a `push_notifications` method which will find the associated and not yet pushed notifications 123 | and actually push them. 124 | 125 | app = 126 | # Create several notifications for your app 127 | app.push_notifications 128 | 129 | As this task can be potentially very time consuming you will probably want to invoke it in 130 | a background worker 131 | 132 | And that's all!! I hope you find this gem useful and please, feel free to contribute with 133 | any improvement/bug you may find. 134 | 135 | ## Pending tasks 136 | 137 | Please, feel free to contribute in building any of those or adding more stuff to this list! 138 | 139 | * Better generation of the data for each kind of notification. Hiding the particulars 140 | to avoid bugs. 141 | * Rake task to push all notifications from all applications. 142 | 143 | ## Contributing 144 | 145 | 1. Fork it ( https://github.com/calonso/rails-push-notifications/fork ) 146 | 2. Create your feature branch (`git checkout -b my-new-feature`) 147 | 3. Commit your changes (`git commit -am 'Add some feature'`) 148 | 4. Push to the branch (`git push origin my-new-feature`) 149 | 5. Create a new Pull Request 150 | 151 | Released under the MIT-LICENSE. 152 | 153 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | require "bundler/setup" 3 | require 'bundler' 4 | require 'appraisal' 5 | 6 | require 'rspec/core/rake_task' 7 | 8 | RSpec::Core::RakeTask.new(:spec) 9 | 10 | task :default => :spec 11 | 12 | if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"] 13 | task :default => :appraisal 14 | end 15 | 16 | -------------------------------------------------------------------------------- /gemfiles/rails3.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "codeclimate-test-reporter", :group => :test, :require => nil 6 | gem "appraisal" 7 | gem "rails", "~> 3.2.0" 8 | gem "test-unit" 9 | 10 | gemspec :path => "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails4_0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "codeclimate-test-reporter", :group => :test, :require => nil 6 | gem "appraisal" 7 | gem "rails", "~> 4.0.0" 8 | 9 | gemspec :path => "../" 10 | -------------------------------------------------------------------------------- /gemfiles/rails4_1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "codeclimate-test-reporter", :group => :test, :require => nil 6 | gem "appraisal" 7 | gem "rails", "~> 4.1.0" 8 | 9 | gemspec :path => "../" 10 | -------------------------------------------------------------------------------- /gemfiles/rails4_2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "http://rubygems.org" 4 | 5 | gem "codeclimate-test-reporter", :group => :test, :require => nil 6 | gem "appraisal" 7 | gem "rails", "~> 4.2.0" 8 | 9 | gemspec :path => "../" 10 | -------------------------------------------------------------------------------- /lib/generators/rails-push-notifications/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Generate migrations needed to create RailsPushNotifications tables 3 | 4 | Example: 5 | rails generate rails_push_notifications:migrations 6 | 7 | -------------------------------------------------------------------------------- /lib/generators/rails-push-notifications/migrations_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators' 2 | require 'rails/generators/migration' 3 | 4 | module RailsPushNotifications 5 | module Generators 6 | class MigrationsGenerator < Rails::Generators::Base 7 | include Rails::Generators::Migration 8 | 9 | source_root File.expand_path('../templates/migrations', __FILE__) 10 | 11 | def create_migrations 12 | templates = [ 13 | 'create_rails_push_notifications_apps', 14 | 'create_rails_push_notifications_notifications' 15 | ] 16 | 17 | templates.each do |file| 18 | migration_template("#{file}.rb", "db/migrate/#{file}.rb") 19 | end 20 | end 21 | 22 | def self.next_migration_number(path) 23 | @count ||= 0 24 | @count += 1 25 | (Time.now.utc.strftime("%Y%m%d%H%M%S").to_i + @count).to_s 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/generators/rails-push-notifications/templates/migrations/create_rails_push_notifications_apps.rb: -------------------------------------------------------------------------------- 1 | class CreateRailsPushNotificationsApps < ActiveRecord::Migration 2 | def self.up 3 | create_table :rails_push_notifications_apns_apps do |t| 4 | t.text :apns_dev_cert 5 | t.text :apns_prod_cert 6 | t.boolean :sandbox_mode, deafult: true 7 | 8 | t.timestamps null: false 9 | end 10 | 11 | create_table :rails_push_notifications_gcm_apps do |t| 12 | t.string :gcm_key 13 | 14 | t.timestamps null: false 15 | end 16 | 17 | create_table :rails_push_notifications_mpns_apps do |t| 18 | t.text :cert 19 | 20 | t.timestamps null: false 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/generators/rails-push-notifications/templates/migrations/create_rails_push_notifications_notifications.rb: -------------------------------------------------------------------------------- 1 | class CreateRailsPushNotificationsNotifications < ActiveRecord::Migration 2 | def change 3 | create_table :rails_push_notifications_notifications do |t| 4 | t.text :destinations 5 | t.integer :app_id 6 | t.string :app_type 7 | t.text :data 8 | t.text :results 9 | t.integer :success 10 | t.integer :failed 11 | t.boolean :sent, default: false 12 | 13 | t.timestamps null: false 14 | end 15 | 16 | add_index( 17 | :rails_push_notifications_notifications, 18 | [:app_id, :app_type, :sent], 19 | name: 'app_and_sent_index_on_rails_push_notifications' 20 | ) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/rails-push-notifications.rb: -------------------------------------------------------------------------------- 1 | require 'ruby-push-notifications' 2 | require 'rails-push-notifications/apps' 3 | require 'rails-push-notifications/notification' 4 | require 'rails-push-notifications/rails_push_notifications_railtie' 5 | -------------------------------------------------------------------------------- /lib/rails-push-notifications/apps.rb: -------------------------------------------------------------------------------- 1 | require 'rails-push-notifications/apps/base_app' 2 | require 'rails-push-notifications/apps/apns_app' 3 | require 'rails-push-notifications/apps/gcm_app' 4 | require 'rails-push-notifications/apps/mpns_app' 5 | -------------------------------------------------------------------------------- /lib/rails-push-notifications/apps/apns_app.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | # 3 | # This class represents an Apple iOS application. 4 | # 5 | # @author Carlos Alonso 6 | # 7 | class APNSApp < BaseApp 8 | self.table_name = 'rails_push_notifications_apns_apps' 9 | 10 | # Requires a development certificate 11 | validates :apns_dev_cert, presence: true 12 | # Requires a production certificate 13 | validates :apns_prod_cert, presence: true 14 | 15 | before_validation { |app| app.sandbox_mode = true if app.sandbox_mode.nil? } 16 | 17 | private 18 | 19 | # @return [String] the certificate(dev or prod) to use 20 | def cert 21 | sandbox_mode ? apns_dev_cert : apns_prod_cert 22 | end 23 | 24 | # @return [RubyPushNotifications::APNS::APNSPusher] configured and 25 | # ready to push 26 | def build_pusher 27 | RubyPushNotifications::APNS::APNSPusher.new cert, sandbox_mode 28 | end 29 | 30 | # @return [RubyPushNotifications::APNS::APNSNotification]. The type of 31 | # notifications this app manages 32 | def notification_type 33 | RubyPushNotifications::APNS::APNSNotification 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/rails-push-notifications/apps/base_app.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | # 3 | # Abstract. This class is the base of all application entity type. 4 | # 5 | # @author Carlos Alonso 6 | # 7 | class BaseApp < ActiveRecord::Base 8 | 9 | self.abstract_class = true 10 | 11 | # has_many notifications 12 | has_many :notifications, as: :app 13 | 14 | # 15 | # This method will find all notifications owned by this app and 16 | # push them. 17 | # 18 | def push_notifications 19 | pending = find_pending 20 | to_send = pending.map do |notification| 21 | notification_type.new notification.destinations, notification.data 22 | end 23 | pusher = build_pusher 24 | pusher.push to_send 25 | pending.each_with_index do |p, i| 26 | p.update_attributes! results: to_send[i].results 27 | end 28 | end 29 | 30 | protected 31 | 32 | # 33 | # Method that searches the owned notifications for those not yet sent 34 | # 35 | def find_pending 36 | notifications.where sent: false 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/rails-push-notifications/apps/gcm_app.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | # 3 | # This class represents an Android GCM application. 4 | # 5 | # @author Carlos Alonso 6 | # 7 | class GCMApp < BaseApp 8 | self.table_name = 'rails_push_notifications_gcm_apps' 9 | 10 | # Requires a gcm_key 11 | validates :gcm_key, presence: true 12 | 13 | private 14 | 15 | # @return [RubyPushNotifications::GCM::GCMPusher] configured and 16 | # ready to push 17 | def build_pusher 18 | RubyPushNotifications::GCM::GCMPusher.new gcm_key 19 | end 20 | 21 | # @return [RubyPushNotifications::GCM::GCMNotification]. The type of 22 | # notifications this app manages 23 | def notification_type 24 | RubyPushNotifications::GCM::GCMNotification 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rails-push-notifications/apps/mpns_app.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | # 3 | # This class represents an Windows Phone application. 4 | # 5 | # @author Carlos Alonso 6 | # 7 | class MPNSApp < BaseApp 8 | self.table_name = 'rails_push_notifications_mpns_apps' 9 | 10 | # Requires the certificate 11 | validates :cert, presence: true 12 | 13 | private 14 | 15 | # @return [RubyPushNotifications::MPNS::MPNSPusher] configured and 16 | # ready to push 17 | def build_pusher 18 | RubyPushNotifications::MPNS::MPNSPusher.new cert 19 | end 20 | 21 | # @return [RubyPushNotifications::MPNS::MPNSNotification]. The type of 22 | # notifications this app manages 23 | def notification_type 24 | RubyPushNotifications::MPNS::MPNSNotification 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rails-push-notifications/notification.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | # 3 | # This class represents the Notification Entity. 4 | # 5 | # @author Carlos Alonso 6 | # 7 | class Notification < ActiveRecord::Base 8 | 9 | self.table_name = 'rails_push_notifications_notifications' 10 | 11 | # belongs_to an app 12 | belongs_to :app, polymorphic: true 13 | 14 | # Requires an app 15 | validates :app, presence: true 16 | # Requires data 17 | validates :data, presence: true 18 | # Requires at least one destination 19 | validates :destinations, presence: true, length: { minimum: 1 } 20 | 21 | serialize :data, Hash 22 | serialize :destinations, Array 23 | serialize :results 24 | 25 | # Automatically manages the sent flag, setting it to true as soon as 26 | # it is assigned results. 27 | before_save do |record| 28 | record.sent = !record.results.nil? 29 | true 30 | end 31 | 32 | # Automatically manages the success and failed fields, setting them to the 33 | # value specified by the results assigned. 34 | before_save do |record| 35 | if record.results_changed? && record.results 36 | record.success = record.results.success 37 | record.failed = record.results.failed 38 | end 39 | true 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/rails-push-notifications/rails_push_notifications_railtie.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | # 3 | # This class exports the migrations generator to Rails 4 | # 5 | # @author Carlos Alonso 6 | # 7 | class RailsPushNotificationsRailtie < Rails::Railtie 8 | generators do 9 | require 'generators/rails-push-notifications/migrations_generator' 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/rails-push-notifications/version.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | VERSION = '0.2.3' 3 | end 4 | -------------------------------------------------------------------------------- /rails-push-notifications.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path('../lib', __FILE__) 2 | 3 | # Maintain your gem's version: 4 | require 'rails-push-notifications/version' 5 | 6 | # Describe your gem and declare its dependencies: 7 | Gem::Specification.new do |s| 8 | s.name = 'rails-push-notifications' 9 | s.version = RailsPushNotifications::VERSION 10 | s.authors = ['Carlos Alonso'] 11 | s.email = ['info@mrcalonso.com'] 12 | s.homepage = 'https://github.com/calonso/rails-push-notifications' 13 | s.summary = 'Professional iOS, Android and Windows Phone push notifications for Ruby on Rails' 14 | s.description = 'Rails gem to push notifications for iOS, Android and Windows Phone devices' 15 | 16 | s.files = Dir['{lib}/**/*'] + %w(MIT-LICENSE Rakefile README.md) 17 | 18 | s.test_files = Dir["spec/**/*"] 19 | s.require_paths = ["lib"] 20 | 21 | s.license = 'MIT' 22 | 23 | s.required_ruby_version = '>= 1.9.3' 24 | 25 | s.add_dependency 'rails', '>= 3.2' 26 | s.add_dependency 'ruby-push-notifications', '~> 0.3' 27 | 28 | s.add_development_dependency 'sqlite3', '~> 1.3' 29 | s.add_development_dependency 'rspec', '~> 3.2' 30 | s.add_development_dependency 'rspec-rails', '~> 3.0' 31 | s.add_development_dependency 'factory_girl', '~> 4.0' 32 | s.add_development_dependency 'generator_spec', '~> 0.9' 33 | s.add_development_dependency 'webmock', '~> 1.20' 34 | end 35 | -------------------------------------------------------------------------------- /spec/apps/apns_app_spec.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | describe APNSApp, type: :model do 3 | it 'creates an instance given valid attributes' do 4 | APNSApp.create! attributes_for :apns_app 5 | end 6 | 7 | describe 'validations' do 8 | 9 | let(:app) { build :apns_app } 10 | 11 | it 'requires an apns dev cert' do 12 | app.apns_dev_cert = nil 13 | expect(app).to_not be_valid 14 | end 15 | 16 | it 'requires an apns prod cert' do 17 | app.apns_prod_cert = nil 18 | expect(app).to_not be_valid 19 | end 20 | 21 | describe 'sandbox mode' do 22 | it 'automatically sets sandbox mode if missing' do 23 | app.sandbox_mode = nil 24 | expect do 25 | app.valid? 26 | end.to change { app.sandbox_mode }.from(nil).to true 27 | end 28 | 29 | it 'uses the value if given' do 30 | app.sandbox_mode = false 31 | expect do 32 | app.valid? 33 | end.to_not change { app.sandbox_mode }.from false 34 | end 35 | end 36 | end 37 | 38 | describe 'notifications relationship' do 39 | let(:app) { create :apns_app } 40 | 41 | it 'can create new notifications' do 42 | expect do 43 | app.notifications.create attributes_for :apns_notification 44 | end.to change { app.reload.notifications.count }.from(0).to 1 45 | end 46 | end 47 | 48 | describe '#push_notifications' do 49 | let(:app) { create :apns_app } 50 | let(:notifications) do 51 | (1..10).map { create :apns_notification, app: app } 52 | end 53 | let(:single_notification) { create :apns_notification, app: app } 54 | 55 | let(:connection) do 56 | instance_double(RubyPushNotifications::APNS::APNSConnection). 57 | as_null_object 58 | end 59 | 60 | before do 61 | allow(RubyPushNotifications::APNS::APNSConnection). 62 | to receive(:open).with(app.apns_dev_cert, app.sandbox_mode). 63 | and_return connection 64 | allow(IO).to receive(:select).and_return [] 65 | end 66 | 67 | it 'assigns results' do 68 | expect do 69 | app.push_notifications 70 | end.to change { 71 | notifications.map do |n| 72 | n.reload.results && n.results.individual_results 73 | end 74 | }.from([nil] * 10). 75 | to([[RubyPushNotifications::APNS::NO_ERROR_STATUS_CODE]] * 10) 76 | end 77 | 78 | it 'marks as sent' do 79 | expect do 80 | app.push_notifications 81 | end.to change { single_notification.reload.sent }.from(false).to true 82 | end 83 | 84 | context 'with an already sent notification' do 85 | let!(:sent_notification) { create :apns_notification, app: app } 86 | 87 | before { app.push_notifications } 88 | 89 | it "doesn't send already sent notifications" do 90 | expect(connection).to_not receive(:write) 91 | app.push_notifications 92 | end 93 | end 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /spec/apps/gcm_app_spec.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | describe GCMApp, type: :model do 3 | 4 | it 'creates an instance given valid attributes' do 5 | GCMApp.create! attributes_for :gcm_app 6 | end 7 | 8 | describe 'validations' do 9 | let(:app) { build :gcm_app } 10 | 11 | it 'requires a gcm key' do 12 | app.gcm_key = nil 13 | expect(app).to_not be_valid 14 | end 15 | end 16 | 17 | describe 'notifications relationship' do 18 | let(:app) { create :gcm_app } 19 | 20 | it 'can create new notifications' do 21 | expect do 22 | app.notifications.create attributes_for :gcm_notification 23 | end.to change { app.reload.notifications.count }.from(0).to 1 24 | end 25 | end 26 | 27 | describe '#push_notifications' do 28 | let(:app) { create :gcm_app } 29 | let(:notifications) do 30 | (1..10).map { create :gcm_notification, app: app } 31 | end 32 | let(:single_notification) { create :gcm_notification, app: app } 33 | 34 | let(:response) { JSON.dump a: 1 } 35 | 36 | before do 37 | stub_request(:post, 'https://android.googleapis.com/gcm/send'). 38 | to_return status: [200, 'OK'], body: response 39 | end 40 | 41 | it 'assigns results' do 42 | expect do 43 | app.push_notifications 44 | end.to change { 45 | notifications.map { |n| n.reload.results } 46 | }.from([nil] * 10). 47 | to([RubyPushNotifications::GCM::GCMResponse.new(200, response)] * 10) 48 | end 49 | 50 | it 'marks as sent' do 51 | expect do 52 | app.push_notifications 53 | end.to change { single_notification.reload.sent }.from(false).to true 54 | end 55 | 56 | context 'with an already sent notification' do 57 | let!(:sent_notification) { create :apns_notification, app: app } 58 | 59 | before { app.push_notifications } 60 | 61 | it "doesn't send already sent notifications" do 62 | expect(RubyPushNotifications::GCM::GCMConnection). 63 | to_not receive(:post) 64 | app.push_notifications 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /spec/apps/mpns_app_spec.rb: -------------------------------------------------------------------------------- 1 | module RailsPushNotifications 2 | describe MPNSApp, type: :model do 3 | 4 | it 'creates an instance given valid attributes' do 5 | MPNSApp.create! attributes_for :mpns_app 6 | end 7 | 8 | describe 'validations' do 9 | 10 | let(:app) { build :mpns_app } 11 | 12 | it 'requires the certificate' do 13 | app.cert = nil 14 | expect(app).to_not be_valid 15 | end 16 | end 17 | 18 | describe 'notifications relationship' do 19 | 20 | let(:app) { create :mpns_app } 21 | 22 | it 'can create new notifications' do 23 | expect do 24 | app.notifications.create attributes_for :mpns_notification 25 | end.to change { app.reload.notifications.count }.from(0).to 1 26 | end 27 | end 28 | 29 | describe '#push_notifications' do 30 | 31 | let(:app) { create :mpns_app } 32 | let(:notifications) { 33 | (1..10).map { create :mpns_notification, app: app } 34 | } 35 | let(:single_notification) { create :mpns_notification, app: app } 36 | 37 | let(:headers) { 38 | { 39 | 'x-notificationstatus' => 'Received', 40 | 'x-deviceconnectionstatus' => 'Connected', 41 | 'x-subscriptionstatus' => 'Active' 42 | } 43 | } 44 | let(:response) { [ { device_url: 'http://s.notify.live.net/1', headers: headers, code: 200 } ]} 45 | 46 | before do 47 | stub_request(:post, %r{s.notify.live.net}). 48 | to_return status: [200, 'OK'], headers: headers 49 | end 50 | 51 | it 'assigns results' do 52 | expect do 53 | app.push_notifications 54 | end.to change { notifications.map { |n| n.reload.results } }.from([nil] * 10).to([RubyPushNotifications::MPNS::MPNSResponse.new(response)] * 10) 55 | end 56 | 57 | it 'marks as sent' do 58 | expect do 59 | app.push_notifications 60 | end.to change { single_notification.reload.sent }.from(false).to true 61 | end 62 | 63 | context 'with an already sent notification' do 64 | let!(:sent_notification) { create :mpns_notification, app: app } 65 | 66 | before { app.push_notifications } 67 | 68 | it "doesn't send already sent notifications" do 69 | expect(RubyPushNotifications::MPNS::MPNSConnection).to_not receive(:post) 70 | app.push_notifications 71 | end 72 | end 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /spec/factories/apps.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :apns_app, class: 'RailsPushNotifications::APNSApp' do 3 | apns_dev_cert 'abc' 4 | apns_prod_cert 'def' 5 | sandbox_mode true 6 | end 7 | 8 | factory :gcm_app, class: 'RailsPushNotifications::GCMApp' do 9 | gcm_key 'abc123def456' 10 | end 11 | 12 | factory :mpns_app, class: 'RailsPushNotifications::MPNSApp' do 13 | cert 'abc' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/factories/notifications.rb: -------------------------------------------------------------------------------- 1 | 2 | FactoryGirl.define do 3 | factory :apns_notification, class: 'RailsPushNotifications::Notification' do 4 | association :app, factory: :apns_app, strategy: :create 5 | data a: 1 6 | destinations ['1'] 7 | end 8 | 9 | factory :gcm_notification, class: 'RailsPushNotifications::Notification' do 10 | association :app, factory: :gcm_app, strategy: :create 11 | data a: 1 12 | destinations ['1'] 13 | end 14 | 15 | factory :mpns_notification, class: 'RailsPushNotifications::Notification' do 16 | association :app, factory: :mpns_app, strategy: :create 17 | data message: { value1: 'hello' } 18 | destinations ['http://s.notify.live.net/1'] 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/generators/migrations_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rails-push-notifications/migrations_generator' 2 | 3 | describe RailsPushNotifications::Generators::MigrationsGenerator, type: :generator do 4 | destination File.expand_path("../tmp", __FILE__) 5 | 6 | before do 7 | prepare_destination 8 | run_generator 9 | end 10 | 11 | after do 12 | rm_rf destination_root 13 | end 14 | 15 | it 'creates the migrations' do 16 | expect(destination_root).to have_structure do 17 | directory 'db' do 18 | directory 'migrate' do 19 | migration 'create_rails_push_notifications_apps' 20 | migration 'create_rails_push_notifications_notifications' 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/notification_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | module RailsPushNotifications 3 | describe Notification, type: :model do 4 | 5 | class FakeResults 6 | attr_reader :value1, :success, :failed 7 | 8 | def initialize(value1, success, failed) 9 | @value1 = value1 10 | @success = success 11 | @failed = failed 12 | end 13 | 14 | def ==(other) 15 | other != nil && other.is_a?(FakeResults) && 16 | @value1 == other.value1 && @success == other.success && 17 | @failed == failed || super(other) 18 | end 19 | end 20 | 21 | let(:notification) { build :apns_notification } 22 | 23 | describe 'app relationship' do 24 | it 'requires an app' do 25 | notification.app = nil 26 | expect(notification).to_not be_valid 27 | end 28 | 29 | it 'belongs to an app' do 30 | expect(notification.app).to be_a APNSApp 31 | end 32 | end 33 | 34 | describe 'data' do 35 | it 'requires data' do 36 | notification.data = nil 37 | expect(notification).to_not be_valid 38 | end 39 | 40 | it 'has to be a Hash' do 41 | expect do 42 | notification.data = 'dummy text' 43 | notification.save 44 | end.to raise_error ActiveRecord::SerializationTypeMismatch 45 | end 46 | end 47 | 48 | describe 'destinations' do 49 | it 'requires destinations' do 50 | notification.destinations = nil 51 | expect(notification).to_not be_valid 52 | end 53 | 54 | it 'has to be an array' do 55 | expect do 56 | notification.destinations = 'dummy destinations' 57 | notification.save 58 | end.to raise_error ActiveRecord::SerializationTypeMismatch 59 | end 60 | 61 | it 'has to contain at least one destination' do 62 | notification.destinations = [] 63 | expect(notification).to_not be_valid 64 | end 65 | end 66 | 67 | describe 'sent' do 68 | it 'is false by default' do 69 | notification.save 70 | expect(notification.sent).to eq false 71 | end 72 | 73 | it 'is sent if results assigned' do 74 | notification.save 75 | expect do 76 | notification.results = FakeResults.new(1, 2, 3) 77 | notification.save 78 | end.to change { notification.reload.sent }.from(false).to true 79 | end 80 | end 81 | 82 | describe 'results' do 83 | 84 | let(:success) { 1 } 85 | let(:failed) { 3 } 86 | let(:results) { FakeResults.new 'abc', success, failed } 87 | 88 | it 'serializes any object' do 89 | notification.results = results 90 | notification.save 91 | notification.reload 92 | expect(notification.results).to eq results 93 | expect(notification.success).to eq success 94 | expect(notification.failed).to eq failed 95 | end 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /spec/rails_apps/rails3_2.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'rails/all' 3 | require 'action_view/testing/resolvers' 4 | require 'rails/test_help' 5 | 6 | require 'rails-push-notifications' 7 | 8 | class RailsPushNotificationsApp < Rails::Application 9 | config.root = File.expand_path("../../..", __FILE__) 10 | config.cache_classes = true 11 | 12 | config.eager_load = false 13 | config.serve_static_assets = true 14 | config.static_cache_control = "public, max-age=3600" 15 | 16 | config.consider_all_requests_local = true 17 | config.action_controller.perform_caching = false 18 | 19 | config.action_dispatch.show_exceptions = false 20 | 21 | config.action_controller.allow_forgery_protection = false 22 | 23 | config.active_support.deprecation = :stderr 24 | 25 | config.middleware.delete "Rack::Lock" 26 | config.middleware.delete "ActionDispatch::Flash" 27 | config.middleware.delete "ActionDispatch::BestStandardsSupport" 28 | config.secret_token = "49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk" 29 | routes.append do 30 | get "/" => "welcome#index" 31 | end 32 | end 33 | 34 | class ApplicationController < ActionController::Base 35 | include Rails.application.routes.url_helpers 36 | layout 'application' 37 | self.view_paths = [ActionView::FixtureResolver.new( 38 | "layouts/application.html.erb" => '<%= yield %>', 39 | "welcome/index.html.erb"=> 'Hello from index.html.erb', 40 | )] 41 | 42 | def index 43 | end 44 | 45 | end 46 | 47 | RailsPushNotificationsApp.initialize! 48 | -------------------------------------------------------------------------------- /spec/rails_apps/rails4.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'rails/all' 3 | require 'action_view/testing/resolvers' 4 | 5 | require 'rails-push-notifications' 6 | 7 | module RailsPushNotificationsApp 8 | class Application < Rails::Application 9 | config.root = File.expand_path("../../..", __FILE__) 10 | config.cache_classes = true 11 | 12 | config.eager_load = false 13 | config.static_cache_control = "public, max-age=3600" 14 | 15 | config.consider_all_requests_local = true 16 | config.action_controller.perform_caching = false 17 | 18 | config.action_dispatch.show_exceptions = false 19 | 20 | config.action_controller.allow_forgery_protection = false 21 | 22 | config.active_support.deprecation = :stderr 23 | 24 | config.middleware.delete "Rack::Lock" 25 | config.middleware.delete "ActionDispatch::Flash" 26 | config.middleware.delete "ActionDispatch::BestStandardsSupport" 27 | config.secret_key_base = '49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk' 28 | routes.append do 29 | get "/" => "application#index" 30 | end 31 | end 32 | end 33 | 34 | class ApplicationController < ActionController::Base 35 | include Rails.application.routes.url_helpers 36 | layout 'application' 37 | self.view_paths = [ActionView::FixtureResolver.new( 38 | "layouts/application.html.erb" => '<%= yield %>', 39 | "welcome/index.html.erb"=> 'Hello from index.html.erb', 40 | )] 41 | 42 | def index 43 | end 44 | 45 | end 46 | 47 | RailsPushNotificationsApp::Application.initialize! 48 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'codeclimate-test-reporter' 3 | CodeClimate::TestReporter.start 4 | 5 | Bundler.setup 6 | 7 | require 'rails' 8 | 9 | ENV['RAILS_ENV'] = 'test' 10 | 11 | case "#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}" 12 | when '3.2' 13 | ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:' 14 | require 'rails_apps/rails3_2' 15 | require 'active_record/migration' 16 | when '4.0' 17 | ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:' 18 | require 'rails_apps/rails4' 19 | when '4.1', '4.2' 20 | ENV['DATABASE_URL'] = 'sqlite3::memory:' 21 | require 'rails_apps/rails4' 22 | else 23 | raise NotImplementedError.new "Rails Push Notifications gem doesn't support Rails #{Rails.version}" 24 | end 25 | 26 | Bundler.require :default 27 | Bundler.require :development 28 | 29 | require 'webmock/rspec' 30 | require 'rspec/rails' 31 | require 'ruby-push-notifications' 32 | 33 | Dir["./spec/support/**/*.rb"].sort.each { |f| require f } 34 | 35 | files = Dir.glob(File.join(File.dirname(__FILE__), '..', 'lib', 'generators', 'rails-push-notifications', 'templates', 'migrations', '*.rb')) 36 | 37 | migrations = [] 38 | files.each_with_index do |file, version| 39 | name, scope = file.scan(/([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first 40 | 41 | name = name.camelize 42 | 43 | migrations << ActiveRecord::MigrationProxy.new(name, version, file, scope) 44 | end 45 | 46 | migrations.sort_by(&:version) 47 | 48 | migrator = ActiveRecord::Migrator.new(:up, migrations) 49 | 50 | if Rails::VERSION::MAJOR == 3 51 | migrator.instance_variable_set(:@migrations, migrations) 52 | end 53 | 54 | migrator.migrate 55 | 56 | RSpec.configure do |config| 57 | config.use_transactional_fixtures = true 58 | config.order = :random 59 | end 60 | 61 | WebMock.disable_net_connect!(:allow => "codeclimate.com") 62 | -------------------------------------------------------------------------------- /spec/support/factory_girl.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include FactoryGirl::Syntax::Methods 3 | end 4 | 5 | FactoryGirl.find_definitions 6 | --------------------------------------------------------------------------------